├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── LICENSE ├── README.md ├── build.gradle ├── core ├── build.gradle └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── temporal │ │ │ └── samples │ │ │ ├── apikey │ │ │ ├── ApiKeyWorker.java │ │ │ ├── MyWorkflow.java │ │ │ ├── MyWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── asyncchild │ │ │ ├── ChildWorkflow.java │ │ │ ├── ChildWorkflowImpl.java │ │ │ ├── ParentWorkflow.java │ │ │ ├── ParentWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── asyncuntypedchild │ │ │ ├── ChildWorkflow.java │ │ │ ├── ChildWorkflowImpl.java │ │ │ ├── ParentWorkflow.java │ │ │ ├── ParentWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── batch │ │ │ ├── heartbeatingactivity │ │ │ │ ├── HeartbeatingActivityBatchStarter.java │ │ │ │ ├── HeartbeatingActivityBatchWorker.java │ │ │ │ ├── HeartbeatingActivityBatchWorkflow.java │ │ │ │ ├── HeartbeatingActivityBatchWorkflowImpl.java │ │ │ │ ├── README.md │ │ │ │ ├── RecordLoader.java │ │ │ │ ├── RecordLoaderImpl.java │ │ │ │ ├── RecordProcessor.java │ │ │ │ ├── RecordProcessorActivity.java │ │ │ │ ├── RecordProcessorActivityImpl.java │ │ │ │ ├── RecordProcessorImpl.java │ │ │ │ └── SingleRecord.java │ │ │ ├── iterator │ │ │ │ ├── IteratorBatchStarter.java │ │ │ │ ├── IteratorBatchWorker.java │ │ │ │ ├── IteratorBatchWorkflow.java │ │ │ │ ├── IteratorBatchWorkflowImpl.java │ │ │ │ ├── README.md │ │ │ │ ├── RecordLoader.java │ │ │ │ ├── RecordLoaderImpl.java │ │ │ │ ├── RecordProcessorWorkflow.java │ │ │ │ ├── RecordProcessorWorkflowImpl.java │ │ │ │ └── SingleRecord.java │ │ │ └── slidingwindow │ │ │ │ ├── BatchProgress.java │ │ │ │ ├── BatchWorkflow.java │ │ │ │ ├── BatchWorkflowImpl.java │ │ │ │ ├── ProcessBatchInput.java │ │ │ │ ├── README.md │ │ │ │ ├── RecordIterable.java │ │ │ │ ├── RecordLoader.java │ │ │ │ ├── RecordLoaderImpl.java │ │ │ │ ├── RecordProcessorWorkflow.java │ │ │ │ ├── RecordProcessorWorkflowImpl.java │ │ │ │ ├── SingleRecord.java │ │ │ │ ├── SlidingWindowBatchStarter.java │ │ │ │ ├── SlidingWindowBatchWorker.java │ │ │ │ ├── SlidingWindowBatchWorkflow.java │ │ │ │ └── SlidingWindowBatchWorkflowImpl.java │ │ │ ├── bookingsaga │ │ │ ├── Booking.java │ │ │ ├── README.md │ │ │ ├── TripBookingActivities.java │ │ │ ├── TripBookingActivitiesImpl.java │ │ │ ├── TripBookingClient.java │ │ │ ├── TripBookingWorker.java │ │ │ ├── TripBookingWorkflow.java │ │ │ └── TripBookingWorkflowImpl.java │ │ │ ├── bookingsyncsaga │ │ │ ├── Booking.java │ │ │ ├── README.md │ │ │ ├── TripBookingActivities.java │ │ │ ├── TripBookingActivitiesImpl.java │ │ │ ├── TripBookingClient.java │ │ │ ├── TripBookingWorker.java │ │ │ ├── TripBookingWorkflow.java │ │ │ └── TripBookingWorkflowImpl.java │ │ │ ├── common │ │ │ └── QueryWorkflowExecution.java │ │ │ ├── countinterceptor │ │ │ ├── ClientCounter.java │ │ │ ├── InterceptorStarter.java │ │ │ ├── README.md │ │ │ ├── SimpleClientCallsInterceptor.java │ │ │ ├── SimpleClientInterceptor.java │ │ │ ├── SimpleCountActivityInboundCallsInterceptor.java │ │ │ ├── SimpleCountWorkerInterceptor.java │ │ │ ├── SimpleCountWorkflowInboundCallsInterceptor.java │ │ │ ├── SimpleCountWorkflowOutboundCallsInterceptor.java │ │ │ ├── WorkerCounter.java │ │ │ ├── activities │ │ │ │ ├── MyActivities.java │ │ │ │ └── MyActivitiesImpl.java │ │ │ └── workflow │ │ │ │ ├── MyChildWorkflow.java │ │ │ │ ├── MyChildWorkflowImpl.java │ │ │ │ ├── MyWorkflow.java │ │ │ │ └── MyWorkflowImpl.java │ │ │ ├── customchangeversion │ │ │ ├── CustomChangeVersionActivities.java │ │ │ ├── CustomChangeVersionActivitiesImpl.java │ │ │ ├── CustomChangeVersionStarter.java │ │ │ ├── CustomChangeVersionWorkflow.java │ │ │ ├── CustomChangeVersionWorkflowImpl.java │ │ │ └── README.md │ │ │ ├── dsl │ │ │ ├── DslActivities.java │ │ │ ├── DslActivitiesImpl.java │ │ │ ├── DslWorkflow.java │ │ │ ├── DslWorkflowImpl.java │ │ │ ├── README.md │ │ │ ├── Starter.java │ │ │ └── model │ │ │ │ ├── Flow.java │ │ │ │ └── FlowAction.java │ │ │ ├── earlyreturn │ │ │ ├── EarlyReturnClient.java │ │ │ ├── EarlyReturnWorker.java │ │ │ ├── README.md │ │ │ ├── Transaction.java │ │ │ ├── TransactionActivities.java │ │ │ ├── TransactionActivitiesImpl.java │ │ │ ├── TransactionRequest.java │ │ │ ├── TransactionWorkflow.java │ │ │ ├── TransactionWorkflowImpl.java │ │ │ └── TxResult.java │ │ │ ├── encodefailures │ │ │ ├── CustomerAgeCheck.java │ │ │ ├── CustomerAgeCheckImpl.java │ │ │ ├── InvalidCustomerException.java │ │ │ ├── MyCustomer.java │ │ │ ├── README.md │ │ │ ├── SimplePrefixPayloadCodec.java │ │ │ └── Starter.java │ │ │ ├── encryptedpayloads │ │ │ ├── CryptCodec.java │ │ │ └── EncryptedPayloadsActivity.java │ │ │ ├── excludefrominterceptor │ │ │ ├── README.md │ │ │ ├── RunMyWorkflows.java │ │ │ ├── activities │ │ │ │ ├── ForInterceptorActivities.java │ │ │ │ ├── ForInterceptorActivitiesImpl.java │ │ │ │ ├── MyActivities.java │ │ │ │ └── MyActivitiesImpl.java │ │ │ ├── interceptor │ │ │ │ ├── MyActivityInboundCallsInterceptor.java │ │ │ │ ├── MyWorkerInterceptor.java │ │ │ │ ├── MyWorkflowInboundCallsInterceptor.java │ │ │ │ └── MyWorkflowOutboundCallsInterceptor.java │ │ │ └── workflows │ │ │ │ ├── MyWorkflow.java │ │ │ │ ├── MyWorkflowOne.java │ │ │ │ ├── MyWorkflowOneImpl.java │ │ │ │ ├── MyWorkflowTwo.java │ │ │ │ └── MyWorkflowTwoImpl.java │ │ │ ├── fileprocessing │ │ │ ├── FileProcessingStarter.java │ │ │ ├── FileProcessingWorker.java │ │ │ ├── FileProcessingWorkflow.java │ │ │ ├── FileProcessingWorkflowImpl.java │ │ │ ├── README.md │ │ │ ├── StoreActivities.java │ │ │ └── StoreActivitiesImpl.java │ │ │ ├── getresultsasync │ │ │ ├── MyWorkflow.java │ │ │ ├── MyWorkflowImpl.java │ │ │ ├── README.md │ │ │ ├── Starter.java │ │ │ └── Worker.java │ │ │ ├── hello │ │ │ ├── HelloAccumulator.java │ │ │ ├── HelloActivity.java │ │ │ ├── HelloActivityExclusiveChoice.java │ │ │ ├── HelloActivityRetry.java │ │ │ ├── HelloAsync.java │ │ │ ├── HelloAsyncActivityCompletion.java │ │ │ ├── HelloAsyncLambda.java │ │ │ ├── HelloAwait.java │ │ │ ├── HelloCancellationScope.java │ │ │ ├── HelloCancellationScopeWithTimer.java │ │ │ ├── HelloChild.java │ │ │ ├── HelloCron.java │ │ │ ├── HelloDelayedStart.java │ │ │ ├── HelloDetachedCancellationScope.java │ │ │ ├── HelloDynamic.java │ │ │ ├── HelloEagerWorkflowStart.java │ │ │ ├── HelloException.java │ │ │ ├── HelloLocalActivity.java │ │ │ ├── HelloParallelActivity.java │ │ │ ├── HelloPeriodic.java │ │ │ ├── HelloPolymorphicActivity.java │ │ │ ├── HelloQuery.java │ │ │ ├── HelloSaga.java │ │ │ ├── HelloSchedules.java │ │ │ ├── HelloSearchAttributes.java │ │ │ ├── HelloSideEffect.java │ │ │ ├── HelloSignal.java │ │ │ ├── HelloSignalWithTimer.java │ │ │ ├── HelloTypedSearchAttributes.java │ │ │ ├── HelloUpdate.java │ │ │ ├── HelloWorkflowTimer.java │ │ │ └── README.md │ │ │ ├── keymanagementencryption │ │ │ └── awsencryptionsdk │ │ │ │ ├── EncryptedPayloads.java │ │ │ │ ├── KeyringCodec.java │ │ │ │ └── README.md │ │ │ ├── listworkflows │ │ │ ├── Customer.java │ │ │ ├── CustomerActivities.java │ │ │ ├── CustomerActivitiesImpl.java │ │ │ ├── CustomerWorkflow.java │ │ │ ├── CustomerWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── metrics │ │ │ ├── MetricsStarter.java │ │ │ ├── MetricsUtils.java │ │ │ ├── MetricsWorker.java │ │ │ ├── README.md │ │ │ ├── activities │ │ │ │ ├── MetricsActivities.java │ │ │ │ └── MetricsActivitiesImpl.java │ │ │ └── workflow │ │ │ │ ├── MetricsWorkflow.java │ │ │ │ └── MetricsWorkflowImpl.java │ │ │ ├── moneybatch │ │ │ ├── Account.java │ │ │ ├── AccountActivityWorker.java │ │ │ ├── AccountImpl.java │ │ │ ├── AccountTransferWorker.java │ │ │ ├── AccountTransferWorkflow.java │ │ │ ├── AccountTransferWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── TransferRequester.java │ │ │ ├── moneytransfer │ │ │ ├── Account.java │ │ │ ├── AccountActivityWorker.java │ │ │ ├── AccountImpl.java │ │ │ ├── AccountTransferWorker.java │ │ │ ├── AccountTransferWorkflow.java │ │ │ ├── AccountTransferWorkflowImpl.java │ │ │ ├── README.MD │ │ │ └── TransferRequester.java │ │ │ ├── nexus │ │ │ ├── README.MD │ │ │ ├── caller │ │ │ │ ├── CallerStarter.java │ │ │ │ ├── CallerWorker.java │ │ │ │ ├── EchoCallerWorkflow.java │ │ │ │ ├── EchoCallerWorkflowImpl.java │ │ │ │ ├── HelloCallerWorkflow.java │ │ │ │ └── HelloCallerWorkflowImpl.java │ │ │ ├── handler │ │ │ │ ├── HandlerWorker.java │ │ │ │ ├── HelloHandlerWorkflow.java │ │ │ │ ├── HelloHandlerWorkflowImpl.java │ │ │ │ └── NexusServiceImpl.java │ │ │ ├── options │ │ │ │ └── ClientOptions.java │ │ │ └── service │ │ │ │ ├── NexusService.java │ │ │ │ └── description.md │ │ │ ├── nexuscancellation │ │ │ ├── README.MD │ │ │ ├── caller │ │ │ │ ├── CallerStarter.java │ │ │ │ ├── CallerWorker.java │ │ │ │ ├── HelloCallerWorkflow.java │ │ │ │ └── HelloCallerWorkflowImpl.java │ │ │ └── handler │ │ │ │ ├── HandlerWorker.java │ │ │ │ └── HelloHandlerWorkflowImpl.java │ │ │ ├── nexuscontextpropagation │ │ │ ├── README.MD │ │ │ ├── caller │ │ │ │ ├── CallerStarter.java │ │ │ │ ├── CallerWorker.java │ │ │ │ ├── EchoCallerWorkflowImpl.java │ │ │ │ └── HelloCallerWorkflowImpl.java │ │ │ ├── handler │ │ │ │ ├── HandlerWorker.java │ │ │ │ ├── HelloHandlerWorkflowImpl.java │ │ │ │ └── NexusServiceImpl.java │ │ │ └── propagation │ │ │ │ ├── MDCContextPropagator.java │ │ │ │ └── NexusMDCContextInterceptor.java │ │ │ ├── payloadconverter │ │ │ ├── cloudevents │ │ │ │ ├── CEWorkflow.java │ │ │ │ ├── CEWorkflowImpl.java │ │ │ │ ├── CloudEventsPayloadConverter.java │ │ │ │ ├── README.md │ │ │ │ └── Starter.java │ │ │ └── crypto │ │ │ │ ├── CryptoWorkflow.java │ │ │ │ ├── CryptoWorkflowImpl.java │ │ │ │ ├── MyCustomer.java │ │ │ │ ├── README.md │ │ │ │ └── Starter.java │ │ │ ├── peractivityoptions │ │ │ ├── FailingActivities.java │ │ │ ├── FailingActivitiesImpl.java │ │ │ ├── PerActivityOptionsWorkflow.java │ │ │ ├── PerActivityOptionsWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── polling │ │ │ ├── PollingActivities.java │ │ │ ├── PollingWorkflow.java │ │ │ ├── README.md │ │ │ ├── TestService.java │ │ │ ├── frequent │ │ │ │ ├── FrequentPollingActivityImpl.java │ │ │ │ ├── FrequentPollingStarter.java │ │ │ │ ├── FrequentPollingWorkflowImpl.java │ │ │ │ └── README.md │ │ │ ├── infrequent │ │ │ │ ├── InfrequentPollingActivityImpl.java │ │ │ │ ├── InfrequentPollingStarter.java │ │ │ │ ├── InfrequentPollingWorkflowImpl.java │ │ │ │ └── README.md │ │ │ ├── infrequentwithretryafter │ │ │ │ ├── InfrequentPollingWithRetryAfterActivityImpl.java │ │ │ │ ├── InfrequentPollingWithRetryAfterStarter.java │ │ │ │ ├── InfrequentPollingWithRetryAfterWorkflowImpl.java │ │ │ │ └── README.md │ │ │ └── periodicsequence │ │ │ │ ├── PeriodicPollingActivityImpl.java │ │ │ │ ├── PeriodicPollingChildWorkflowImpl.java │ │ │ │ ├── PeriodicPollingStarter.java │ │ │ │ ├── PeriodicPollingWorkflowImpl.java │ │ │ │ ├── PollingChildWorkflow.java │ │ │ │ └── README.md │ │ │ ├── retryonsignalinterceptor │ │ │ ├── FailureRequester.java │ │ │ ├── MyActivity.java │ │ │ ├── MyActivityImpl.java │ │ │ ├── MyWorkflow.java │ │ │ ├── MyWorkflowImpl.java │ │ │ ├── MyWorkflowWorker.java │ │ │ ├── QueryRequester.java │ │ │ ├── README.MD │ │ │ ├── RetryOnSignalInterceptorListener.java │ │ │ ├── RetryOnSignalWorkerInterceptor.java │ │ │ ├── RetryOnSignalWorkflowInboundCallsInterceptor.java │ │ │ ├── RetryOnSignalWorkflowOutboundCallsInterceptor.java │ │ │ └── RetryRequester.java │ │ │ ├── safemessagepassing │ │ │ ├── ClusterManagerActivities.java │ │ │ ├── ClusterManagerActivitiesImpl.java │ │ │ ├── ClusterManagerWorkflow.java │ │ │ ├── ClusterManagerWorkflowImpl.java │ │ │ ├── ClusterManagerWorkflowStarter.java │ │ │ ├── ClusterManagerWorkflowWorker.java │ │ │ └── README.md │ │ │ ├── sleepfordays │ │ │ ├── README.md │ │ │ ├── SendEmailActivity.java │ │ │ ├── SendEmailActivityImpl.java │ │ │ ├── SleepForDaysImpl.java │ │ │ ├── SleepForDaysWorkflow.java │ │ │ ├── Starter.java │ │ │ └── Worker.java │ │ │ ├── ssl │ │ │ ├── MyWorkflow.java │ │ │ ├── MyWorkflowImpl.java │ │ │ ├── README.md │ │ │ ├── SslEnabledWorkerCustomCA.java │ │ │ └── Starter.java │ │ │ ├── terminateworkflow │ │ │ ├── MyWorkflow.java │ │ │ ├── MyWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── Starter.java │ │ │ ├── tracing │ │ │ ├── JaegerUtils.java │ │ │ ├── README.md │ │ │ ├── Starter.java │ │ │ ├── TracingWorker.java │ │ │ └── workflow │ │ │ │ ├── TracingActivities.java │ │ │ │ ├── TracingActivitiesImpl.java │ │ │ │ ├── TracingChildWorkflow.java │ │ │ │ ├── TracingChildWorkflowImpl.java │ │ │ │ ├── TracingWorkflow.java │ │ │ │ └── TracingWorkflowImpl.java │ │ │ └── updatabletimer │ │ │ ├── DynamicSleepWorkflow.java │ │ │ ├── DynamicSleepWorkflowImpl.java │ │ │ ├── DynamicSleepWorkflowStarter.java │ │ │ ├── DynamicSleepWorkflowWorker.java │ │ │ ├── README.md │ │ │ ├── UpdatableTimer.java │ │ │ └── WakeUpTimeUpdater.java │ └── resources │ │ ├── dsl │ │ └── sampleflow.json │ │ └── logback.xml │ └── test │ ├── java │ └── io │ │ └── temporal │ │ └── samples │ │ ├── asyncchild │ │ └── AsyncChildTest.java │ │ ├── asyncuntypedchild │ │ └── AsyncUntypedChildTest.java │ │ ├── batch │ │ ├── heartbeatingactivity │ │ │ └── HeartbeatingActivityBatchWorkflowTest.java │ │ ├── iterator │ │ │ └── IteratorIteratorBatchWorkflowTest.java │ │ └── slidingwindow │ │ │ └── SlidingWindowBatchWorkflowTest.java │ │ ├── bookingsaga │ │ └── TripBookingWorkflowTest.java │ │ ├── bookingsyncsaga │ │ └── TripBookingWorkflowTest.java │ │ ├── countinterceptor │ │ ├── ClientCountInterceptorTest.java │ │ └── WorkerCountInterceptorTest.java │ │ ├── dsl │ │ └── DslWorkflowTest.java │ │ ├── earlyreturn │ │ └── TransactionWorkflowTest.java │ │ ├── encodefailures │ │ └── EncodeFailuresTest.java │ │ ├── excludefrominterceptor │ │ └── ExcludeFromInterceptorTest.java │ │ ├── fileprocessing │ │ └── FileProcessingTest.java │ │ ├── getresultsasync │ │ └── GetResultsAsyncTest.java │ │ ├── hello │ │ ├── HelloAccumulatorTest.java │ │ ├── HelloActivityExclusiveChoiceJUnit5Test.java │ │ ├── HelloActivityExclusiveChoiceTest.java │ │ ├── HelloActivityJUnit5Test.java │ │ ├── HelloActivityReplayTest.java │ │ ├── HelloActivityRetryTest.java │ │ ├── HelloActivityTest.java │ │ ├── HelloAsyncActivityCompletionTest.java │ │ ├── HelloAsyncLambdaTest.java │ │ ├── HelloAsyncTest.java │ │ ├── HelloAwaitTest.java │ │ ├── HelloCancellationScopeTest.java │ │ ├── HelloCancellationScopeWithTimerTest.java │ │ ├── HelloChildJUnit5Test.java │ │ ├── HelloChildTest.java │ │ ├── HelloCronTest.java │ │ ├── HelloDelayedStartTest.java │ │ ├── HelloDetachedCancellationScopeTest.java │ │ ├── HelloDynamicActivityJUnit5Test.java │ │ ├── HelloDynamicTest.java │ │ ├── HelloEagerWorkflowStartTest.java │ │ ├── HelloExceptionTest.java │ │ ├── HelloLocalActivityTest.java │ │ ├── HelloParallelActivityTest.java │ │ ├── HelloPeriodicTest.java │ │ ├── HelloPolymorphicActivityTest.java │ │ ├── HelloQueryTest.java │ │ ├── HelloSearchAttributesTest.java │ │ ├── HelloSideEffectTest.java │ │ ├── HelloSignalTest.java │ │ ├── HelloSignalWithTimerTest.java │ │ ├── HelloUpdateTest.java │ │ └── HelloWorkflowTimerTest.java │ │ ├── listworkflows │ │ └── ListWorkflowsTest.java │ │ ├── metrics │ │ └── MetricsTest.java │ │ ├── moneybatch │ │ └── TransferWorkflowTest.java │ │ ├── moneytransfer │ │ └── TransferWorkflowTest.java │ │ ├── nexus │ │ └── caller │ │ │ └── CallerWorkflowTest.java │ │ ├── payloadconverter │ │ ├── CloudEventsPayloadConverterTest.java │ │ └── CryptoPayloadConverterTest.java │ │ ├── peractivityoptions │ │ └── PerActivityOptionsTest.java │ │ ├── polling │ │ ├── FrequentPollingTest.java │ │ ├── InfrequentPollingTest.java │ │ └── PeriodicPollingTest.java │ │ ├── retryonsignalinterceptor │ │ └── RetryOnSignalInterceptorTest.java │ │ ├── safemessagepassing │ │ └── ClusterManagerWorkflowWorkerTest.java │ │ ├── sleepfordays │ │ ├── SleepForDaysJUnit5Test.java │ │ └── SleepForDaysTest.java │ │ ├── terminateworkflow │ │ └── TerminateWorkflowTest.java │ │ └── tracing │ │ └── TracingTest.java │ └── resources │ └── dsl │ └── sampleflow.json ├── docker └── github │ ├── Dockerfile │ ├── README.md │ └── docker-compose.yaml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── springboot-basic ├── build.gradle └── src │ ├── main │ ├── java │ │ └── io │ │ │ └── temporal │ │ │ └── samples │ │ │ └── springboot │ │ │ ├── SamplesController.java │ │ │ ├── TemporalSpringbootSamplesApplication.java │ │ │ └── hello │ │ │ ├── HelloActivity.java │ │ │ ├── HelloActivityImpl.java │ │ │ ├── HelloWorkflow.java │ │ │ ├── HelloWorkflowImpl.java │ │ │ ├── README.md │ │ │ └── model │ │ │ └── Person.java │ └── resources │ │ ├── application-tc.yaml │ │ ├── application.yaml │ │ ├── static │ │ └── js │ │ │ ├── jquery.sse.js │ │ │ └── samplessse.js │ │ └── templates │ │ ├── fragments.html │ │ ├── hello.html │ │ └── index.html │ └── test │ ├── java │ └── io │ │ └── temporal │ │ └── samples │ │ └── springboot │ │ └── HelloSampleTest.java │ └── resources │ └── application.yaml └── springboot ├── build.gradle └── src ├── main ├── java │ └── io │ │ └── temporal │ │ └── samples │ │ └── springboot │ │ ├── SamplesController.java │ │ ├── TemporalSpringbootSamplesApplication.java │ │ ├── actuator │ │ ├── README.md │ │ └── WorkerActuatorEndpoint.java │ │ ├── camel │ │ ├── CamelConfig.java │ │ ├── CamelResource.java │ │ ├── CamelRoutes.java │ │ ├── OfficeOrder.java │ │ ├── OrderActivity.java │ │ ├── OrderActivityImpl.java │ │ ├── OrderRepository.java │ │ ├── OrderWorkflow.java │ │ ├── OrderWorkflowImpl.java │ │ └── README.md │ │ ├── customize │ │ ├── CustomizeActivity.java │ │ ├── CustomizeActivityImpl.java │ │ ├── CustomizeWorkflow.java │ │ ├── CustomizeWorkflowImpl.java │ │ ├── README.md │ │ └── TemporalOptionsConfig.java │ │ ├── hello │ │ ├── HelloActivity.java │ │ ├── HelloActivityImpl.java │ │ ├── HelloWorkflow.java │ │ ├── HelloWorkflowImpl.java │ │ ├── README.md │ │ └── model │ │ │ └── Person.java │ │ ├── kafka │ │ ├── KafkaActivity.java │ │ ├── KafkaActivityImpl.java │ │ ├── KafkaConfig.java │ │ ├── MessageController.java │ │ ├── MessageWorkflow.java │ │ ├── MessageWorkflowImpl.java │ │ └── README.md │ │ ├── metrics │ │ └── README.md │ │ └── update │ │ ├── ProductNotAvailableForAmountException.java │ │ ├── PurchaseActivities.java │ │ ├── PurchaseActivitiesImpl.java │ │ ├── PurchaseWorkflow.java │ │ ├── PurchaseWorkflowImpl.java │ │ ├── README.md │ │ ├── ResourceNotFoundException.java │ │ └── model │ │ ├── Product.java │ │ ├── ProductRepository.java │ │ └── Purchase.java └── resources │ ├── application-tc.yaml │ ├── application.yaml │ ├── data.sql │ ├── static │ └── js │ │ ├── jquery.sse.js │ │ └── samplessse.js │ └── templates │ ├── actuator.html │ ├── camel.html │ ├── customize.html │ ├── fragments.html │ ├── hello.html │ ├── index.html │ ├── kafka.html │ ├── metrics.html │ └── update.html └── test ├── java └── io │ └── temporal │ └── samples │ └── springboot │ ├── CamelSampleTest.java │ ├── CustomizeSampleTest.java │ ├── HelloSampleTest.java │ ├── HelloSampleTestMockedActivity.java │ ├── KafkaConsumerTestHelper.java │ ├── KafkaSampleTest.java │ └── UpdateSampleTest.java └── resources ├── application.yaml └── data.sql /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Primary owners 2 | 3 | * @tsurdilo @temporalio/sdk @antmendoza -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gradle" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "github-actions" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: "Continuous Integration" 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | validation: 6 | name: "Gradle wrapper validation" 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | - uses: gradle/wrapper-validation-action@v3 11 | 12 | unittest: 13 | name: Unit Tests 14 | runs-on: ubuntu-latest 15 | timeout-minutes: 15 16 | steps: 17 | - name: Checkout repo 18 | uses: actions/checkout@v4 19 | with: 20 | fetch-depth: 0 21 | submodules: recursive 22 | ref: ${{ github.event.pull_request.head.sha }} 23 | 24 | - name: Run unit tests 25 | run: | 26 | docker compose -f ./docker/github/docker-compose.yaml up unit-test 27 | 28 | code_format: 29 | name: Code format 30 | runs-on: ubuntu-latest 31 | timeout-minutes: 20 32 | steps: 33 | - name: Checkout repo 34 | uses: actions/checkout@v4 35 | with: 36 | fetch-depth: 0 37 | submodules: recursive 38 | ref: ${{ github.event.pull_request.head.sha }} 39 | 40 | - name: Set up Java 41 | uses: actions/setup-java@v4 42 | with: 43 | java-version: "11" 44 | distribution: "temurin" 45 | 46 | - name: Set up Gradle 47 | uses: gradle/actions/setup-gradle@v3 48 | 49 | - name: Run copyright and code format checks 50 | run: ./gradlew --no-daemon spotlessCheck -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | .*.swp 3 | .*.swo 4 | *.iml 5 | .DS_Store 6 | .idea 7 | .gradle 8 | /build 9 | /core/build 10 | /springboot/build 11 | /springboot-basic/build 12 | /out 13 | /core/out 14 | /springboot/out 15 | /springboot-basic/out 16 | .classpath 17 | .project 18 | .settings/ 19 | bin/ 20 | core/.vscode/ -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Temporal Java SDK 2 | 3 | Copyright (c) 2025 Temporal Technologies, Inc. All Rights Reserved 4 | 5 | Copyright (c) 2017 Uber Technologies, Inc. All Rights Reserved 6 | 7 | Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved 8 | 9 | Licensed under the Apache License, Version 2.0 (the "License"); you may not use this 10 | file except in compliance with the License. You may obtain a copy of the License at 11 | 12 | http://www.apache.org/licenses/LICENSE-2.0 13 | 14 | Unless required by applicable law or agreed to in writing, software distributed under 15 | the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 16 | CONDITIONS OF ANY KIND, either express or implied. See the License for the 17 | specific language governing permissions and limitations under the License. 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/apikey/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.apikey; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyWorkflow { 8 | @WorkflowMethod 9 | String execute(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/apikey/MyWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.apikey; 2 | 3 | public class MyWorkflowImpl implements MyWorkflow { 4 | @Override 5 | public String execute() { 6 | return "done"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncchild/ChildWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncchild; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface ChildWorkflow { 8 | @WorkflowMethod 9 | String executeChild(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncchild/ChildWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncchild; 2 | 3 | import io.temporal.workflow.Workflow; 4 | import java.time.Duration; 5 | 6 | public class ChildWorkflowImpl implements ChildWorkflow { 7 | @Override 8 | public String executeChild() { 9 | Workflow.sleep(Duration.ofSeconds(3)); 10 | return "Child workflow done"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncchild/ParentWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncchild; 2 | 3 | import io.temporal.api.common.v1.WorkflowExecution; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface ParentWorkflow { 9 | @WorkflowMethod 10 | WorkflowExecution executeParent(); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncchild/README.md: -------------------------------------------------------------------------------- 1 | # Async Child Workflow execution 2 | 3 | The sample demonstrates shows how to invoke a Child Workflow asynchronously. 4 | The Child Workflow is allowed to complete its execution even after the Parent Workflow completes. 5 | 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.asyncchild.Starter 8 | ``` 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncuntypedchild/ChildWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncuntypedchild; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | /** 7 | * Define the child workflow Interface. It must contain one method annotated with @WorkflowMethod 8 | * 9 | * @see WorkflowInterface 10 | * @see WorkflowMethod 11 | */ 12 | @WorkflowInterface 13 | public interface ChildWorkflow { 14 | 15 | /** 16 | * Define the child workflow method. This method is executed when the workflow is started. The 17 | * workflow completes when the workflow method finishes execution. 18 | */ 19 | @WorkflowMethod 20 | String composeGreeting(String greeting, String name); 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncuntypedchild/ChildWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncuntypedchild; 2 | 3 | import io.temporal.workflow.Workflow; 4 | 5 | /** 6 | * Define the parent workflow implementation. It implements the getGreeting workflow method 7 | * 8 | *

Note that a workflow implementation must always be public for the Temporal library to be able 9 | * to create its instances. 10 | */ 11 | public class ChildWorkflowImpl implements ChildWorkflow { 12 | 13 | @Override 14 | public String composeGreeting(String greeting, String name) { 15 | 16 | // Sleep for 2 seconds to ensure the child completes after the parent. 17 | Workflow.sleep(2000); 18 | 19 | return greeting + " " + name + "!"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncuntypedchild/ParentWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncuntypedchild; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | /** 7 | * Define the parent workflow interface. It must contain one method annotated with @WorkflowMethod 8 | * 9 | * @see WorkflowInterface 10 | * @see WorkflowMethod 11 | */ 12 | @WorkflowInterface 13 | public interface ParentWorkflow { 14 | 15 | /** 16 | * Define the parent workflow method. This method is executed when the workflow is started. The 17 | * workflow completes when the workflow method finishes execution. 18 | */ 19 | @WorkflowMethod 20 | String getGreeting(String name); 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/asyncuntypedchild/README.md: -------------------------------------------------------------------------------- 1 | # Async Child Workflow execution 2 | 3 | The sample demonstrates shows how to invoke an Untyped Child Workflow asynchronously. 4 | The Child Workflow continues running for some time after the Parent Workflow completes. 5 | 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.asyncuntypedchild.Starter 8 | ``` 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchStarter.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import static io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchWorker.TASK_QUEUE; 4 | 5 | import io.temporal.api.common.v1.WorkflowExecution; 6 | import io.temporal.client.WorkflowClient; 7 | import io.temporal.client.WorkflowOptions; 8 | import io.temporal.serviceclient.WorkflowServiceStubs; 9 | 10 | /** Starts a single execution of HeartbeatingActivityBatchWorkflow. */ 11 | public class HeartbeatingActivityBatchStarter { 12 | 13 | public static void main(String[] args) { 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | WorkflowClient workflowClient = WorkflowClient.newInstance(service); 16 | WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build(); 17 | HeartbeatingActivityBatchWorkflow batchWorkflow = 18 | workflowClient.newWorkflowStub(HeartbeatingActivityBatchWorkflow.class, options); 19 | WorkflowExecution execution = WorkflowClient.start(batchWorkflow::processBatch); 20 | System.out.println( 21 | "Started batch workflow. WorkflowId=" 22 | + execution.getWorkflowId() 23 | + ", RunId=" 24 | + execution.getRunId()); 25 | System.exit(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | /** 9 | * A worker process that hosts implementations of HeartbeatingActivityBatchWorkflow and 10 | * RecordProcessorActivity. 11 | */ 12 | public final class HeartbeatingActivityBatchWorker { 13 | 14 | static final String TASK_QUEUE = "HeartbeatingActivityBatch"; 15 | 16 | public static void main(String[] args) { 17 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 18 | WorkflowClient client = WorkflowClient.newInstance(service); 19 | 20 | WorkerFactory factory = WorkerFactory.newInstance(client); 21 | Worker worker = factory.newWorker(TASK_QUEUE); 22 | 23 | worker.registerWorkflowImplementationTypes(HeartbeatingActivityBatchWorkflowImpl.class); 24 | 25 | worker.registerActivitiesImplementations( 26 | new RecordProcessorActivityImpl(new RecordLoaderImpl(), new RecordProcessorImpl())); 27 | factory.start(); 28 | System.out.println("Worker started for task queue: " + TASK_QUEUE); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface HeartbeatingActivityBatchWorkflow { 8 | 9 | /** 10 | * Processes the batch of records. 11 | * 12 | * @return total number of processed records. 13 | */ 14 | @WorkflowMethod 15 | int processBatch(); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/README.md: -------------------------------------------------------------------------------- 1 | A sample implementation of processing a batch by an Activity. 2 | 3 | An Activity can run as long as needed. 4 | It reports that it is still alive through Heartbeat. 5 | 6 | If the Worker is restarted, the Activity is retried after the Heartbeat Timeout. 7 | 8 | Temporal allows store data in Heartbeat _details_. 9 | These details are available to the next Activity attempt. 10 | The progress of the record processing is stored in the details to avoid reprocessing records from the beginning on failures. 11 | 12 | #### Running the Iterator Batch Sample 13 | 14 | The sample has two executables. Execute each command in a separate terminal window. 15 | 16 | The first command runs the Worker that hosts the Workflow and Activity Executions. Restart the worker while the batch is 17 | executing to see how activity timeout and retry work. 18 | 19 | ```bash 20 | ./gradlew -q execute -PmainClass=io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchWorker 21 | ``` 22 | 23 | The second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution. 24 | 25 | ```bash 26 | ./gradlew -q execute -PmainClass=io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchStarter 27 | ``` 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordLoader.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import java.util.Optional; 4 | 5 | /** 6 | * Helper class that is used to iterate over a list of records. 7 | * 8 | *

A specific implementation depends on a use case. For example, it can execute an SQL DB query 9 | * or read a comma delimited file. More complex use cases would need passing a different type of 10 | * offset parameter. 11 | */ 12 | public interface RecordLoader { 13 | 14 | /** 15 | * Returns the next record. 16 | * 17 | * @param offset offset of the next record. 18 | * @return Record at the offset. Empty optional if offset exceeds the dataset size. 19 | */ 20 | Optional getRecord(int offset); 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordLoaderImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import java.util.Optional; 4 | 5 | /** Fake implementation of RecordLoader. */ 6 | public final class RecordLoaderImpl implements RecordLoader { 7 | 8 | static final int RECORD_COUNT = 1000; 9 | 10 | @Override 11 | public Optional getRecord(int offset) { 12 | if (offset >= RECORD_COUNT) { 13 | return Optional.empty(); 14 | } 15 | return Optional.of(new SingleRecord(offset)); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | /** A helper class that implements record processing. */ 4 | public interface RecordProcessor { 5 | 6 | /** 7 | * Processes a single record. 8 | * 9 | * @param record record to process 10 | */ 11 | void processRecord(SingleRecord record); 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessorActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface RecordProcessorActivity { 7 | 8 | /** Processes all records in the dataset */ 9 | int processRecords(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessorImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | /** Fake record processor implementation. */ 7 | public final class RecordProcessorImpl implements RecordProcessor { 8 | 9 | private static final Logger log = LoggerFactory.getLogger(RecordProcessorImpl.class); 10 | 11 | @Override 12 | public void processRecord(SingleRecord record) { 13 | // Fake processing logic 14 | try { 15 | Thread.sleep(100); 16 | log.info("Processed " + record); 17 | } catch (InterruptedException ignored) { 18 | return; 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/SingleRecord.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.heartbeatingactivity; 2 | 3 | /** Record to process. A real application would add a use case specific data. */ 4 | public class SingleRecord { 5 | private int id; 6 | 7 | public SingleRecord(int id) { 8 | this.id = id; 9 | } 10 | 11 | /** JSON deserializer needs it */ 12 | public SingleRecord() {} 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "Record{" + "id=" + id + '}'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchStarter.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import static io.temporal.samples.batch.iterator.IteratorBatchWorker.TASK_QUEUE; 4 | 5 | import io.temporal.api.common.v1.WorkflowExecution; 6 | import io.temporal.client.WorkflowClient; 7 | import io.temporal.client.WorkflowOptions; 8 | import io.temporal.serviceclient.WorkflowServiceStubs; 9 | 10 | /** Starts a single execution of IteratorBatchWorkflow. */ 11 | public class IteratorBatchStarter { 12 | 13 | public static void main(String[] args) { 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | WorkflowClient workflowClient = WorkflowClient.newInstance(service); 16 | WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build(); 17 | IteratorBatchWorkflow batchWorkflow = 18 | workflowClient.newWorkflowStub(IteratorBatchWorkflow.class, options); 19 | WorkflowExecution execution = WorkflowClient.start(batchWorkflow::processBatch, 5, 0); 20 | System.out.println( 21 | "Started batch workflow. WorkflowId=" 22 | + execution.getWorkflowId() 23 | + ", RunId=" 24 | + execution.getRunId()); 25 | System.exit(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | /** 9 | * A worker process that hosts implementations of IteratorBatchWorkflow and RecordProcessorWorkflow 10 | * as well as RecordLoader activity. 11 | */ 12 | public final class IteratorBatchWorker { 13 | 14 | static final String TASK_QUEUE = "IteratorBatch"; 15 | 16 | public static void main(String[] args) { 17 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 18 | WorkflowClient client = WorkflowClient.newInstance(service); 19 | 20 | WorkerFactory factory = WorkerFactory.newInstance(client); 21 | Worker worker = factory.newWorker(TASK_QUEUE); 22 | 23 | worker.registerWorkflowImplementationTypes( 24 | IteratorBatchWorkflowImpl.class, RecordProcessorWorkflowImpl.class); 25 | 26 | worker.registerActivitiesImplementations(new RecordLoaderImpl()); 27 | factory.start(); 28 | System.out.println("Worker started for task queue: " + TASK_QUEUE); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface IteratorBatchWorkflow { 8 | 9 | /** 10 | * Processes the batch of records. 11 | * 12 | * @param offset the offset of the first record to process. 0 to start the batch processing. 13 | * @param pageSize the number of records to process in a single workflow run. 14 | * @return total number of processed records. 15 | */ 16 | @WorkflowMethod 17 | int processBatch(int pageSize, int offset); 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/README.md: -------------------------------------------------------------------------------- 1 | A sample implementation of the Workflow iterator pattern. 2 | 3 | A workflow starts a configured number of Child Workflows in parallel. Each child processes a single record. 4 | After all children complete, the parent calls continue-as-new and starts the children for the next page of records. 5 | 6 | This allows processing a set of records of any size. The advantage of this approach is simplicity. 7 | The main disadvantage is that it processes records in batches, with each batch waiting for the slowest child workflow. 8 | 9 | A variation of this pattern runs activities instead of child workflows. 10 | 11 | #### Running the Iterator Batch Sample 12 | 13 | The sample has two executables. Execute each command in a separate terminal window. 14 | 15 | The first command runs the Worker that hosts the Workflow and Activity Executions. 16 | 17 | ```bash 18 | ./gradlew -q execute -PmainClass=io.temporal.samples.batch.iterator.IteratorBatchWorker 19 | ``` 20 | 21 | The second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution. 22 | 23 | ```bash 24 | ./gradlew -q execute -PmainClass=io.temporal.samples.batch.iterator.IteratorBatchStarter 25 | ``` 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/RecordLoader.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import java.util.List; 5 | 6 | /** 7 | * Activity that is used to iterate over a list of records. 8 | * 9 | *

A specific implementation depends on a use case. For example, it can execute an SQL DB query 10 | * or read a comma delimited file. More complex use cases would need passing a different type of 11 | * offset parameter. 12 | */ 13 | @ActivityInterface 14 | public interface RecordLoader { 15 | 16 | /** 17 | * Returns the next page of records. 18 | * 19 | * @param offset offset of the next page. 20 | * @param pageSize maximum number of records to return. 21 | * @return empty list if no more records to process. 22 | */ 23 | List getRecords(int pageSize, int offset); 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/RecordLoaderImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** Fake implementation of RecordLoader. */ 7 | public final class RecordLoaderImpl implements RecordLoader { 8 | 9 | // The sample always returns 5 pages. 10 | // The real application would iterate over an existing dataset or file. 11 | public static final int PAGE_COUNT = 5; 12 | 13 | @Override 14 | public List getRecords(int pageSize, int offset) { 15 | List records = new ArrayList<>(pageSize); 16 | if (offset < pageSize * PAGE_COUNT) { 17 | for (int i = 0; i < pageSize; i++) { 18 | records.add(new SingleRecord(offset + i)); 19 | } 20 | } 21 | return records; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/RecordProcessorWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | /** Workflow that implements processing of a single record. */ 7 | @WorkflowInterface 8 | public interface RecordProcessorWorkflow { 9 | 10 | /** Processes a single record */ 11 | @WorkflowMethod 12 | void processRecord(SingleRecord r); 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/RecordProcessorWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | import io.temporal.workflow.Workflow; 4 | import java.time.Duration; 5 | import java.util.Random; 6 | import org.slf4j.Logger; 7 | 8 | /** Fake RecordProcessorWorkflow implementation. */ 9 | public class RecordProcessorWorkflowImpl implements RecordProcessorWorkflow { 10 | public static final Logger log = Workflow.getLogger(RecordProcessorWorkflowImpl.class); 11 | private final Random random = Workflow.newRandom(); 12 | 13 | @Override 14 | public void processRecord(SingleRecord r) { 15 | // Simulate some processing 16 | Workflow.sleep(Duration.ofSeconds(random.nextInt(30))); 17 | log.info("Processed " + r); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/iterator/SingleRecord.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.iterator; 2 | 3 | /** Record to process. A real application would add a use case specific data. */ 4 | public class SingleRecord { 5 | private int id; 6 | 7 | public SingleRecord(int id) { 8 | this.id = id; 9 | } 10 | 11 | /** JSON deserializer needs it */ 12 | public SingleRecord() {} 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "SingleRecord{" + "id=" + id + '}'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/BatchProgress.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import java.util.Set; 4 | 5 | /** Used as a result of {@link SlidingWindowBatchWorkflow#getProgress()} query. */ 6 | public final class BatchProgress { 7 | 8 | private final int progress; 9 | 10 | private final Set currentRecords; 11 | 12 | public BatchProgress(int progress, Set currentRecords) { 13 | this.progress = progress; 14 | this.currentRecords = currentRecords; 15 | } 16 | 17 | /** Count of completed record processing child workflows. */ 18 | public int getProgress() { 19 | return progress; 20 | } 21 | 22 | /** Ids of records that are currently being processed by child workflows. */ 23 | public Set getCurrentRecords() { 24 | return currentRecords; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/BatchWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface BatchWorkflow { 8 | 9 | /** 10 | * Processes a batch of records using multiple parallel sliding window workflows. 11 | * 12 | * @param pageSize the number of records to start processing in a single sliding window workflow 13 | * run. 14 | * @param slidingWindowSize the number of records to process in parallel by a single sliding 15 | * window workflow. Can be larger than the pageSize. 16 | * @param partitions defines the number of SlidingWindowBatchWorkflows to run in parallel. If 17 | * number of partitions is too low the update rate of a single SlidingWindowBatchWorkflows can 18 | * get too high. 19 | * @return total number of processed records. 20 | */ 21 | @WorkflowMethod 22 | int processBatch(int pageSize, int slidingWindowSize, int partitions); 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordLoader.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import java.util.List; 5 | 6 | @ActivityInterface 7 | public interface RecordLoader { 8 | 9 | /** 10 | * Returns the next page of records. 11 | * 12 | * @param offset offset of the next page. 13 | * @param pageSize maximum number of records to return. 14 | * @return empty list if no more records to process. 15 | */ 16 | List getRecords(int pageSize, int offset); 17 | 18 | /** 19 | * Returns the total record count. 20 | * 21 | *

Used to divide record ranges among partitions. Some applications might choose a completely 22 | * different approach for partitioning the data set. 23 | */ 24 | int getRecordCount(); 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordLoaderImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | /** Fake loader implementation. The real application would iterate over a dataset or file. */ 7 | public final class RecordLoaderImpl implements RecordLoader { 8 | 9 | private static final int TOTAL_COUNT = 300; 10 | 11 | @Override 12 | public List getRecords(int pageSize, int offset) { 13 | List records = new ArrayList<>(pageSize); 14 | if (offset < TOTAL_COUNT) { 15 | for (int i = offset; i < Math.min(offset + pageSize, TOTAL_COUNT); i++) { 16 | records.add(new SingleRecord(i)); 17 | } 18 | } 19 | return records; 20 | } 21 | 22 | @Override 23 | public int getRecordCount() { 24 | return TOTAL_COUNT; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordProcessorWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | /** Workflow that implements processing of a single record. */ 7 | @WorkflowInterface 8 | public interface RecordProcessorWorkflow { 9 | 10 | /** 11 | * Processes a single record. Must report completion to a parent through {@link 12 | * SlidingWindowBatchWorkflow#reportCompletion(int)} 13 | */ 14 | @WorkflowMethod 15 | void processRecord(SingleRecord r); 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/SingleRecord.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | /** Record to process. */ 4 | public class SingleRecord { 5 | private int id; 6 | 7 | public SingleRecord(int id) { 8 | this.id = id; 9 | } 10 | 11 | /** Needed for JSON deserialization. */ 12 | public SingleRecord() {} 13 | 14 | public int getId() { 15 | return id; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return "SingleRecord{" + "id=" + id + '}'; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchStarter.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import static io.temporal.samples.batch.slidingwindow.SlidingWindowBatchWorker.TASK_QUEUE; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.client.WorkflowOptions; 7 | import io.temporal.serviceclient.WorkflowServiceStubs; 8 | 9 | public class SlidingWindowBatchStarter { 10 | 11 | @SuppressWarnings("CatchAndPrintStackTrace") 12 | public static void main(String[] args) { 13 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 14 | WorkflowClient workflowClient = WorkflowClient.newInstance(service); 15 | WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build(); 16 | BatchWorkflow batchWorkflow = workflowClient.newWorkflowStub(BatchWorkflow.class, options); 17 | WorkflowClient.start(batchWorkflow::processBatch, 10, 25, 3); 18 | System.out.println("Started batch workflow with 3 partitions"); 19 | System.exit(0); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | /** Hosts sliding window batch sample workflow and activity implementations. */ 9 | public final class SlidingWindowBatchWorker { 10 | 11 | static final String TASK_QUEUE = "SlidingWindow"; 12 | 13 | public static void main(String[] args) { 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | WorkflowClient client = WorkflowClient.newInstance(service); 16 | 17 | WorkerFactory factory = WorkerFactory.newInstance(client); 18 | Worker worker = factory.newWorker(TASK_QUEUE); 19 | 20 | worker.registerWorkflowImplementationTypes( 21 | BatchWorkflowImpl.class, 22 | SlidingWindowBatchWorkflowImpl.class, 23 | RecordProcessorWorkflowImpl.class); 24 | worker.registerActivitiesImplementations(new RecordLoaderImpl()); 25 | 26 | factory.start(); 27 | 28 | System.out.println("Worker started for task queue: " + TASK_QUEUE); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.batch.slidingwindow; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | import io.temporal.workflow.WorkflowInterface; 6 | import io.temporal.workflow.WorkflowMethod; 7 | 8 | @WorkflowInterface 9 | public interface SlidingWindowBatchWorkflow { 10 | 11 | /** 12 | * Process the batch of records. 13 | * 14 | * @return total number of processed records. 15 | */ 16 | @WorkflowMethod 17 | int processBatch(ProcessBatchInput input); 18 | 19 | @SignalMethod 20 | void reportCompletion(int recordId); 21 | 22 | @QueryMethod 23 | BatchProgress getProgress(); 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsaga/Booking.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.bookingsaga; 2 | 3 | public final class Booking { 4 | private String carReservationID; 5 | private String hotelReservationID; 6 | private String flightReservationID; 7 | 8 | /** Empty constructor to keep Jackson serializer happy. */ 9 | public Booking() {} 10 | 11 | public Booking(String carReservationID, String hotelReservationID, String flightReservationID) { 12 | this.carReservationID = carReservationID; 13 | this.hotelReservationID = hotelReservationID; 14 | this.flightReservationID = flightReservationID; 15 | } 16 | 17 | public String getCarReservationID() { 18 | return carReservationID; 19 | } 20 | 21 | public String getHotelReservationID() { 22 | return hotelReservationID; 23 | } 24 | 25 | public String getFlightReservationID() { 26 | return flightReservationID; 27 | } 28 | 29 | @Override 30 | public String toString() { 31 | return "Booking{" 32 | + "carReservationID='" 33 | + carReservationID 34 | + '\'' 35 | + ", hotelReservationID='" 36 | + hotelReservationID 37 | + '\'' 38 | + ", flightReservationID='" 39 | + flightReservationID 40 | + '\'' 41 | + '}'; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsaga/README.md: -------------------------------------------------------------------------------- 1 | ## Saga example: trip booking 2 | 3 | Temporal implementation of 4 | the [Camunda BPMN trip booking example](https://github.com/berndruecker/trip-booking-saga-java) which demonstrates 5 | Temporal approach to SAGA. 6 | 7 | Run the following command to start the sample: 8 | 9 | ```bash 10 | ./gradlew -q execute -PmainClass=io.temporal.samples.bookingsaga.TripBookingSaga 11 | ``` 12 | 13 | Note that the booking is expected to fail to demonstrate the compensation flow. 14 | 15 | Sample unit 16 | testing: [TripBookingWorkflowTest](https://github.com/temporalio/samples-java/blob/main/core/src/test/java/io/temporal/samples/bookingsaga/TripBookingWorkflowTest.java) 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsaga/TripBookingClient.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.bookingsaga; 2 | 3 | import com.google.common.base.Throwables; 4 | import io.temporal.client.WorkflowClient; 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | 8 | public class TripBookingClient { 9 | 10 | static final String TASK_QUEUE = "TripBooking"; 11 | 12 | public static void main(String[] args) { 13 | // gRPC stubs wrapper that talks to the local docker instance of temporal service. 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | // client that can be used to start and signal workflows 16 | WorkflowClient client = WorkflowClient.newInstance(service); 17 | 18 | WorkflowOptions options = 19 | WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId("Booking1").build(); 20 | TripBookingWorkflow trip = client.newWorkflowStub(TripBookingWorkflow.class, options); 21 | try { 22 | Booking booking = trip.bookTrip("trip1"); 23 | System.out.println("Booking: " + booking); 24 | } catch (Exception e) { 25 | System.out.println(Throwables.getStackTraceAsString(e)); 26 | } 27 | System.exit(0); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsaga/TripBookingWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.bookingsaga; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface TripBookingWorkflow { 8 | @WorkflowMethod 9 | Booking bookTrip(String name); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsyncsaga/Booking.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.bookingsyncsaga; 2 | 3 | public final class Booking { 4 | private final String carReservationID; 5 | private final String hotelReservationID; 6 | private final String flightReservationID; 7 | 8 | public Booking(String carReservationID, String hotelReservationID, String flightReservationID) { 9 | this.carReservationID = carReservationID; 10 | this.hotelReservationID = hotelReservationID; 11 | this.flightReservationID = flightReservationID; 12 | } 13 | 14 | public String getCarReservationID() { 15 | return carReservationID; 16 | } 17 | 18 | public String getHotelReservationID() { 19 | return hotelReservationID; 20 | } 21 | 22 | public String getFlightReservationID() { 23 | return flightReservationID; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "Booking{" 29 | + "carReservationID='" 30 | + carReservationID 31 | + '\'' 32 | + ", hotelReservationID='" 33 | + hotelReservationID 34 | + '\'' 35 | + ", flightReservationID='" 36 | + flightReservationID 37 | + '\'' 38 | + '}'; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsyncsaga/README.md: -------------------------------------------------------------------------------- 1 | ## Saga example: synchronous trip booking 2 | 3 | The sample demonstrates low latency workflow with client synchronously waiting for result using an update. 4 | In case of failures the caller is unblocked and workflow continues executing compensations 5 | for as long as needed. 6 | 7 | Run the following command to start the worker: 8 | 9 | ```bash 10 | ./gradlew -q execute -PmainClass=io.temporal.samples.bookingsyncsaga.TripBookingWorker 11 | ``` 12 | 13 | Run the following command to request a booking. 14 | Note that the booking is expected to fail to demonstrate the compensation flow. 15 | 16 | ```bash 17 | ./gradlew -q execute -PmainClass=io.temporal.samples.bookingsyncsaga.TripBookingClient 18 | ``` 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.bookingsyncsaga; 2 | 3 | import io.temporal.workflow.UpdateMethod; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface TripBookingWorkflow { 9 | @WorkflowMethod 10 | void bookTrip(String name); 11 | 12 | /** 13 | * Used to wait for booking completion or failure. After this method returns a failure workflow 14 | * keeps running executing compensations. 15 | * 16 | * @return booking information. 17 | */ 18 | @UpdateMethod 19 | Booking waitForBooking(); 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/README.md: -------------------------------------------------------------------------------- 1 | # Demo Workflow Interceptor 2 | 3 | The sample demonstrates: 4 | - the use of a simple Worker Workflow Interceptor that counts the number of Workflow Executions, Child Workflow Executions, and Activity Executions as well as the number of Signals and Queries. 5 | - the use of a simple Client Workflow Interceptor that counts the number of Workflow Executions as well as the number of Signals, Queries and GetResult invocations. 6 | 7 | Run the following command to start the sample: 8 | 9 | ```bash 10 | ./gradlew -q execute -PmainClass=io.temporal.samples.countinterceptor.InterceptorStarter 11 | ``` 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/SimpleClientInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor; 2 | 3 | import io.temporal.common.interceptors.WorkflowClientCallsInterceptor; 4 | import io.temporal.common.interceptors.WorkflowClientInterceptorBase; 5 | 6 | public class SimpleClientInterceptor extends WorkflowClientInterceptorBase { 7 | 8 | private ClientCounter clientCounter; 9 | 10 | public SimpleClientInterceptor(ClientCounter clientCounter) { 11 | this.clientCounter = clientCounter; 12 | } 13 | 14 | @Override 15 | public WorkflowClientCallsInterceptor workflowClientCallsInterceptor( 16 | WorkflowClientCallsInterceptor next) { 17 | return new SimpleClientCallsInterceptor(next, clientCounter); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountActivityInboundCallsInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor; 2 | 3 | import io.temporal.activity.ActivityExecutionContext; 4 | import io.temporal.common.interceptors.ActivityInboundCallsInterceptor; 5 | import io.temporal.common.interceptors.ActivityInboundCallsInterceptorBase; 6 | 7 | public class SimpleCountActivityInboundCallsInterceptor 8 | extends ActivityInboundCallsInterceptorBase { 9 | 10 | private ActivityExecutionContext activityExecutionContext; 11 | 12 | public SimpleCountActivityInboundCallsInterceptor(ActivityInboundCallsInterceptor next) { 13 | super(next); 14 | } 15 | 16 | @Override 17 | public void init(ActivityExecutionContext context) { 18 | this.activityExecutionContext = context; 19 | super.init(context); 20 | } 21 | 22 | @Override 23 | public ActivityOutput execute(ActivityInput input) { 24 | WorkerCounter.add( 25 | this.activityExecutionContext.getInfo().getWorkflowId(), 26 | WorkerCounter.NUM_OF_ACTIVITY_EXECUTIONS); 27 | return super.execute(input); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountWorkerInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor; 2 | 3 | import io.temporal.common.interceptors.*; 4 | 5 | public class SimpleCountWorkerInterceptor extends WorkerInterceptorBase { 6 | 7 | @Override 8 | public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) { 9 | return new SimpleCountWorkflowInboundCallsInterceptor(next); 10 | } 11 | 12 | @Override 13 | public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) { 14 | return new SimpleCountActivityInboundCallsInterceptor(next); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountWorkflowOutboundCallsInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor; 2 | 3 | import io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor; 4 | import io.temporal.common.interceptors.WorkflowOutboundCallsInterceptorBase; 5 | import io.temporal.workflow.Workflow; 6 | 7 | public class SimpleCountWorkflowOutboundCallsInterceptor 8 | extends WorkflowOutboundCallsInterceptorBase { 9 | 10 | public SimpleCountWorkflowOutboundCallsInterceptor(WorkflowOutboundCallsInterceptor next) { 11 | super(next); 12 | } 13 | 14 | @Override 15 | public ChildWorkflowOutput executeChildWorkflow(ChildWorkflowInput input) { 16 | WorkerCounter.add( 17 | Workflow.getInfo().getWorkflowId(), WorkerCounter.NUM_OF_CHILD_WORKFLOW_EXECUTIONS); 18 | return super.executeChildWorkflow(input); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/activities/MyActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor.activities; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface MyActivities { 7 | String sayHello(String name, String title); 8 | 9 | String sayGoodBye(String name, String title); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/activities/MyActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor.activities; 2 | 3 | public class MyActivitiesImpl implements MyActivities { 4 | @Override 5 | public String sayHello(String name, String title) { 6 | return "Hello " + title + " " + name; 7 | } 8 | 9 | @Override 10 | public String sayGoodBye(String name, String title) { 11 | return "Goodbye " + title + " " + name; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyChildWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor.workflow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyChildWorkflow { 8 | @WorkflowMethod 9 | String execChild(String name, String title); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyChildWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor.workflow; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.samples.countinterceptor.activities.MyActivities; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | public class MyChildWorkflowImpl implements MyChildWorkflow { 9 | @Override 10 | public String execChild(String name, String title) { 11 | MyActivities activities = 12 | Workflow.newActivityStub( 13 | MyActivities.class, 14 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build()); 15 | 16 | String result = activities.sayHello(name, title); 17 | result += activities.sayGoodBye(name, title); 18 | 19 | return result; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.countinterceptor.workflow; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | import io.temporal.workflow.WorkflowInterface; 6 | import io.temporal.workflow.WorkflowMethod; 7 | 8 | @WorkflowInterface 9 | public interface MyWorkflow { 10 | @WorkflowMethod 11 | String exec(); 12 | 13 | @SignalMethod 14 | void signalNameAndTitle(String greeting, String title); 15 | 16 | @SignalMethod 17 | void exit(); 18 | 19 | @QueryMethod 20 | String queryName(); 21 | 22 | @QueryMethod 23 | String queryTitle(); 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.customchangeversion; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface CustomChangeVersionActivities { 7 | String customOne(String input); 8 | 9 | String customTwo(String input); 10 | 11 | String customThree(String input); 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.customchangeversion; 2 | 3 | public class CustomChangeVersionActivitiesImpl implements CustomChangeVersionActivities { 4 | @Override 5 | public String customOne(String input) { 6 | return "\ncustomOne activity - " + input; 7 | } 8 | 9 | @Override 10 | public String customTwo(String input) { 11 | return "\ncustomTwo activity - " + input; 12 | } 13 | 14 | @Override 15 | public String customThree(String input) { 16 | return "\ncustomThree activity - " + input; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.customchangeversion; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface CustomChangeVersionWorkflow { 8 | @WorkflowMethod 9 | String run(String input); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/customchangeversion/README.md: -------------------------------------------------------------------------------- 1 | ## Custom Change Version Search Attribute Sample 2 | 3 | This sample shows how to upsert custom search attribute when adding a version change to your workflow code. 4 | It is a current workaround until feature https://github.com/temporalio/sdk-java/issues/587 is implemented. 5 | Purpose of upserting a custom search attribute when addint new versions is to then be able to use 6 | visibility api to search for running/completed executions which are on a specific version. It is also useful to see 7 | if there are no running executions on specific change version in order to remove certain no longer used versioned change 8 | if/else block from your workflow code, so it no longer has to be maintained. 9 | 10 | To run this sample: 11 | ```bash 12 | ./gradlew -q execute -PmainClass=io.temporal.samples.customchangeversion.CustomChangeVersionStarter 13 | ``` 14 | 15 | After running this sample you can go to your Web UI or use Temporal CLI to search for specific CustomChangeVersion, for example: 16 | 17 | ``` 18 | temporal workflow list -q "CustomChangeVersion='add-v3-activity-change-1'" 19 | ``` -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/dsl/DslActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.dsl; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface DslActivities { 7 | String one(); 8 | 9 | String two(); 10 | 11 | String three(); 12 | 13 | String four(); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/dsl/DslActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.dsl; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | 5 | public class DslActivitiesImpl implements DslActivities { 6 | @Override 7 | public String one() { 8 | sleep(1); 9 | return "Activity one done..."; 10 | } 11 | 12 | @Override 13 | public String two() { 14 | sleep(1); 15 | return "Activity two done..."; 16 | } 17 | 18 | @Override 19 | public String three() { 20 | sleep(1); 21 | return "Activity three done..."; 22 | } 23 | 24 | @Override 25 | public String four() { 26 | sleep(1); 27 | return "Activity four done..."; 28 | } 29 | 30 | private void sleep(int seconds) { 31 | try { 32 | Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); 33 | } catch (InterruptedException ee) { 34 | // Empty 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/dsl/DslWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.dsl; 2 | 3 | import io.temporal.samples.dsl.model.Flow; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface DslWorkflow { 9 | @WorkflowMethod 10 | String run(Flow flow, String input); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/dsl/README.md: -------------------------------------------------------------------------------- 1 | # DSL Sample 2 | 3 | This sample shows how to use a DSL on top of Temporal. 4 | The sample defines a number of domain specific json samples 5 | which are used to define steps of actions to be performed by our workflow. 6 | 7 | As with all samples, use the following at your own risk. 8 | 9 | As a rule, DSLs provide limited and restrictive functionality. 10 | They are not suitable for full expressive development. 11 | 12 | In many cases, it's better to build customized DSLs to optimize simplicity and domain targeting for your particular use case. 13 | 14 | ## Run the sample 15 | 16 | 1Start the Starter 17 | 18 | ```bash 19 | ./gradlew -q execute -PmainClass=io.temporal.samples.dsl.Starter 20 | ``` -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/dsl/model/Flow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.dsl.model; 2 | 3 | import java.util.List; 4 | 5 | public class Flow { 6 | private String id; 7 | private String name; 8 | private String description; 9 | private List actions; 10 | 11 | public Flow() {} 12 | 13 | public Flow(String id, String name, String description, List actions) { 14 | this.id = id; 15 | this.name = name; 16 | this.description = description; 17 | this.actions = actions; 18 | } 19 | 20 | public String getId() { 21 | return id; 22 | } 23 | 24 | public void setId(String id) { 25 | this.id = id; 26 | } 27 | 28 | public String getName() { 29 | return name; 30 | } 31 | 32 | public void setName(String name) { 33 | this.name = name; 34 | } 35 | 36 | public String getDescription() { 37 | return description; 38 | } 39 | 40 | public void setDescription(String description) { 41 | this.description = description; 42 | } 43 | 44 | public List getActions() { 45 | return actions; 46 | } 47 | 48 | public void setActions(List actions) { 49 | this.actions = actions; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturnWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.earlyreturn; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.worker.Worker; 5 | import io.temporal.worker.WorkerFactory; 6 | 7 | public class EarlyReturnWorker { 8 | private static final String TASK_QUEUE = "EarlyReturnTaskQueue"; 9 | 10 | public static void main(String[] args) { 11 | WorkflowClient client = EarlyReturnClient.setupWorkflowClient(); 12 | startWorker(client); 13 | } 14 | 15 | private static void startWorker(WorkflowClient client) { 16 | WorkerFactory factory = WorkerFactory.newInstance(client); 17 | Worker worker = factory.newWorker(TASK_QUEUE); 18 | 19 | worker.registerWorkflowImplementationTypes(TransactionWorkflowImpl.class); 20 | worker.registerActivitiesImplementations(new TransactionActivitiesImpl()); 21 | 22 | factory.start(); 23 | System.out.println("Worker started"); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/README.md: -------------------------------------------------------------------------------- 1 | ### Early-Return Sample 2 | 3 | This sample demonstrates an early-return from a workflow. 4 | 5 | By utilizing Update-with-Start, a client can start a new workflow and synchronously receive 6 | a response mid-workflow, while the workflow continues to run to completion. 7 | 8 | To run the sample, start the worker: 9 | ```bash 10 | ./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnWorker 11 | ``` 12 | 13 | Then, start the client: 14 | 15 | ```bash 16 | ./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnClient 17 | ``` 18 | 19 | * The client will start a workflow using Update-With-Start. 20 | * Update-With-Start will trigger an initialization step. 21 | * If the initialization step succeeds (default), intialization will return to the client with a transaction ID and the workflow will continue. The workflow will then complete and return the final result. 22 | * If the intitialization step fails (amount <= 0), the workflow will return to the client with an error message and the workflow will run an activity to cancel the transaction. 23 | 24 | To trigger a failed initialization, set the amount to <= 0 in the `EarlyReturnClient` class's `runWorkflowWithUpdateWithStart` method and re-run the client. -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.earlyreturn; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import io.temporal.activity.ActivityMethod; 5 | 6 | @ActivityInterface 7 | public interface TransactionActivities { 8 | @ActivityMethod 9 | Transaction mintTransactionId(TransactionRequest txRequest); 10 | 11 | @ActivityMethod 12 | Transaction initTransaction(Transaction tx); 13 | 14 | @ActivityMethod 15 | void cancelTransaction(Transaction tx); 16 | 17 | @ActivityMethod 18 | void completeTransaction(Transaction tx); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/TransactionRequest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.earlyreturn; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public final class TransactionRequest { 7 | private final String sourceAccount; 8 | private final String targetAccount; 9 | private final int amount; 10 | 11 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) 12 | public TransactionRequest( 13 | @JsonProperty("sourceAccount") String sourceAccount, 14 | @JsonProperty("targetAccount") String targetAccount, 15 | @JsonProperty("amount") int amount) { 16 | this.sourceAccount = sourceAccount; 17 | this.targetAccount = targetAccount; 18 | this.amount = amount; 19 | } 20 | 21 | @JsonProperty("sourceAccount") 22 | public String getSourceAccount() { 23 | return sourceAccount; 24 | } 25 | 26 | @JsonProperty("targetAccount") 27 | public String getTargetAccount() { 28 | return targetAccount; 29 | } 30 | 31 | @JsonProperty("amount") 32 | public int getAmount() { 33 | return amount; 34 | } 35 | 36 | @Override 37 | public String toString() { 38 | return String.format( 39 | "TransactionRequest{sourceAccount='%s', targetAccount='%s', amount=%d}", 40 | sourceAccount, targetAccount, amount); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/TransactionWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.earlyreturn; 2 | 3 | import io.temporal.workflow.UpdateMethod; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface TransactionWorkflow { 9 | @WorkflowMethod 10 | TxResult processTransaction(TransactionRequest txRequest); 11 | 12 | @UpdateMethod(name = "early-return") 13 | TxResult returnInitResult(); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/earlyreturn/TxResult.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.earlyreturn; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonProperty; 5 | 6 | public class TxResult { 7 | private final String transactionId; 8 | private final String status; 9 | 10 | // Jackson-compatible constructor with @JsonCreator and @JsonProperty annotations 11 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) 12 | public TxResult( 13 | @JsonProperty("transactionId") String transactionId, @JsonProperty("status") String status) { 14 | this.transactionId = transactionId; 15 | this.status = status; 16 | } 17 | 18 | @JsonProperty("transactionId") 19 | public String getTransactionId() { 20 | return transactionId; 21 | } 22 | 23 | @JsonProperty("status") 24 | public String getStatus() { 25 | return status; 26 | } 27 | 28 | @Override 29 | public String toString() { 30 | return String.format("InitResult{transactionId='%s', status='%s'}", transactionId, status); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/encodefailures/CustomerAgeCheck.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.encodefailures; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface CustomerAgeCheck { 8 | @WorkflowMethod 9 | public String validateCustomer(MyCustomer customer); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/encodefailures/CustomerAgeCheckImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.encodefailures; 2 | 3 | import io.temporal.workflow.Workflow; 4 | 5 | public class CustomerAgeCheckImpl implements CustomerAgeCheck { 6 | @Override 7 | public String validateCustomer(MyCustomer customer) { 8 | // Note we have explicitly set InvalidCustomerException type to fail workflow execution 9 | // We wrap it using Workflow.wrap so can throw as unchecked 10 | if (customer.getAge() < 21) { 11 | throw Workflow.wrap( 12 | new InvalidCustomerException("customer " + customer.getName() + " is under age.")); 13 | } else { 14 | return "done..."; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/encodefailures/InvalidCustomerException.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.encodefailures; 2 | 3 | public class InvalidCustomerException extends Exception { 4 | public InvalidCustomerException(String errorMessage) { 5 | super(errorMessage); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/encodefailures/MyCustomer.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.encodefailures; 2 | 3 | public class MyCustomer { 4 | private String name; 5 | private int age; 6 | private boolean approved; 7 | 8 | public MyCustomer() {} 9 | 10 | public MyCustomer(String name, int age) { 11 | this.name = name; 12 | this.age = age; 13 | } 14 | 15 | public String getName() { 16 | return name; 17 | } 18 | 19 | public void setName(String name) { 20 | this.name = name; 21 | } 22 | 23 | public int getAge() { 24 | return age; 25 | } 26 | 27 | public void setAge(int age) { 28 | this.age = age; 29 | } 30 | 31 | public boolean isApproved() { 32 | return approved; 33 | } 34 | 35 | public void setApproved(boolean approved) { 36 | this.approved = approved; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/encodefailures/README.md: -------------------------------------------------------------------------------- 1 | # Using Codec to encode / decode failure messages 2 | 3 | The sample demonstrates how to set up a simple codec for encoding/decoding failure messages 4 | In this sample we set encodeFailureAttributes = true to our CodecDataConverter meaning we want to 5 | encode / decode failure messages as well. 6 | All it does is add a "Customer: " prefix to the message. You can expand on this to add any type of 7 | encoding that you might want to use. 8 | 9 | Our workflow does simple customer age check validation and fails if their age is < 21. 10 | In the Starter then we print out that the failure message client received on execution failure 11 | was indeed encoded using our codec. 12 | 13 | ## Running 14 | 15 | 1. Start Temporal Server with "default" namespace enabled. 16 | For example using local Docker: 17 | 18 | ```bash 19 | git clone https://github.com/temporalio/docker-compose.git 20 | cd docker-compose 21 | docker-compose up 22 | ``` 23 | 24 | 2. Run the following command to start the sample: 25 | 26 | ```bash 27 | ./gradlew -q execute -PmainClass=io.temporal.samples.encodefailures.Starter 28 | ``` 29 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/README.md: -------------------------------------------------------------------------------- 1 | # Excluding certain Workflow and Activity Types from interceptors 2 | 3 | This sample shows how to exclude certain workflow types and Activity types from Workflow and Activity Interceptors. 4 | 5 | 1. Start the Sample: 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.excludefrominterceptor.RunMyWorkflows 8 | ``` 9 | 10 | Observe the event histories of MyWorkflowOne and MyWorkflowTwo in your Temporal Web UI. 11 | You should see that even tho both executions were served by same worker so both had the interceptors applied, 12 | MyWorkflowTwo was excluded from being applied by these interceptors. 13 | 14 | Also from the Activity interceptor logs (System.out prints during sample run) note that 15 | only ActivityOne activity is being intercepted and not ActivityTwo or the "ForInterceptor" activities. 16 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/ForInterceptorActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.activities; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface ForInterceptorActivities { 7 | void forInterceptorActivityOne(Object output); 8 | 9 | void forInterceptorActivityTwo(Object output); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/ForInterceptorActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.activities; 2 | 3 | public class ForInterceptorActivitiesImpl implements ForInterceptorActivities { 4 | @Override 5 | public void forInterceptorActivityOne(Object output) {} 6 | 7 | @Override 8 | public void forInterceptorActivityTwo(Object output) {} 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/MyActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.activities; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface MyActivities { 7 | void activityOne(String input); 8 | 9 | void activityTwo(String input); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/MyActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.activities; 2 | 3 | public class MyActivitiesImpl implements MyActivities { 4 | @Override 5 | public void activityOne(String input) {} 6 | 7 | @Override 8 | public void activityTwo(String input) {} 9 | } 10 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/interceptor/MyWorkerInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.interceptor; 2 | 3 | import io.temporal.common.interceptors.*; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | public class MyWorkerInterceptor extends WorkerInterceptorBase { 8 | private List excludeWorkflowTypes = new ArrayList<>(); 9 | private List excludeActivityTypes = new ArrayList<>(); 10 | 11 | public MyWorkerInterceptor() {} 12 | 13 | public MyWorkerInterceptor(List excludeWorkflowTypes) { 14 | this.excludeWorkflowTypes = excludeWorkflowTypes; 15 | } 16 | 17 | public MyWorkerInterceptor(List excludeWorkflowTypes, List excludeActivityTypes) { 18 | this.excludeWorkflowTypes = excludeWorkflowTypes; 19 | this.excludeActivityTypes = excludeActivityTypes; 20 | } 21 | 22 | @Override 23 | public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) { 24 | return new MyWorkflowInboundCallsInterceptor(excludeWorkflowTypes, next); 25 | } 26 | 27 | @Override 28 | public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) { 29 | return new MyActivityInboundCallsInterceptor(excludeActivityTypes, next); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.workflows; 2 | 3 | import io.temporal.workflow.WorkflowMethod; 4 | 5 | public interface MyWorkflow { 6 | @WorkflowMethod 7 | String execute(String input); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowOne.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.workflows; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | 5 | @WorkflowInterface 6 | public interface MyWorkflowOne extends MyWorkflow {} 7 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowOneImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.workflows; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.samples.excludefrominterceptor.activities.MyActivities; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | public class MyWorkflowOneImpl implements MyWorkflowOne { 9 | private MyActivities activities = 10 | Workflow.newActivityStub( 11 | MyActivities.class, 12 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 13 | 14 | @Override 15 | public String execute(String input) { 16 | activities.activityOne(input); 17 | activities.activityTwo(input); 18 | 19 | return "done"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowTwo.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.workflows; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | 5 | @WorkflowInterface 6 | public interface MyWorkflowTwo extends MyWorkflow {} 7 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowTwoImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.excludefrominterceptor.workflows; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.samples.excludefrominterceptor.activities.MyActivities; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | public class MyWorkflowTwoImpl implements MyWorkflowTwo { 9 | private MyActivities activities = 10 | Workflow.newActivityStub( 11 | MyActivities.class, 12 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 13 | 14 | @Override 15 | public String execute(String input) { 16 | activities.activityOne(input); 17 | activities.activityTwo(input); 18 | 19 | return "done"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/fileprocessing/FileProcessingWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.fileprocessing; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | import java.net.URL; 6 | 7 | /** Contract for file processing workflow. */ 8 | @WorkflowInterface 9 | public interface FileProcessingWorkflow { 10 | @WorkflowMethod 11 | void processFile(URL source, URL destination); 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/fileprocessing/README.md: -------------------------------------------------------------------------------- 1 | Demonstrates how to route tasks to specific Workers. This sample has a set of Activities that download a file, processes it, and uploads the result to a destination. Any Worker can execute the first Activity. However, the second and third Activities must be executed on the same host as the first one. 2 | 3 | #### Running the File Processing Sample 4 | 5 | The sample has two executables. Execute each command in a separate terminal window. 6 | 7 | 8 | The first command runs the Worker that hosts the Workflow and Activity Executions. To demonstrate that Activities execute together, we recommend running more than one instance of this Worker. 9 | 10 | ```bash 11 | ./gradlew -q execute -PmainClass=io.temporal.samples.fileprocessing.FileProcessingWorker 12 | ``` 13 | 14 | The second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution. 15 | 16 | ```bash 17 | ./gradlew -q execute -PmainClass=io.temporal.samples.fileprocessing.FileProcessingStarter 18 | ``` 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/fileprocessing/StoreActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.fileprocessing; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import java.net.URL; 5 | 6 | @ActivityInterface 7 | public interface StoreActivities { 8 | 9 | final class TaskQueueFileNamePair { 10 | private String hostTaskQueue; 11 | private String fileName; 12 | 13 | public TaskQueueFileNamePair(String hostTaskQueue, String fileName) { 14 | this.hostTaskQueue = hostTaskQueue; 15 | this.fileName = fileName; 16 | } 17 | 18 | /** Jackson needs it */ 19 | public TaskQueueFileNamePair() {} 20 | 21 | public String getHostTaskQueue() { 22 | return hostTaskQueue; 23 | } 24 | 25 | public String getFileName() { 26 | return fileName; 27 | } 28 | } 29 | 30 | /** 31 | * Upload file to remote location. 32 | * 33 | * @param localFileName file to upload 34 | * @param url remote location 35 | */ 36 | void upload(String localFileName, URL url); 37 | 38 | /** 39 | * Process file. 40 | * 41 | * @param inputFileName source file name @@return processed file name 42 | */ 43 | String process(String inputFileName); 44 | 45 | /** 46 | * Downloads file to local disk. 47 | * 48 | * @param url remote file location 49 | * @return local task queue and downloaded file name 50 | */ 51 | TaskQueueFileNamePair download(URL url); 52 | } 53 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/getresultsasync/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.getresultsasync; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyWorkflow { 8 | @WorkflowMethod 9 | String justSleep(int seconds); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/getresultsasync/MyWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.getresultsasync; 2 | 3 | import io.temporal.workflow.Workflow; 4 | import java.time.Duration; 5 | 6 | public class MyWorkflowImpl implements MyWorkflow { 7 | @Override 8 | public String justSleep(int seconds) { 9 | Workflow.sleep(Duration.ofSeconds(seconds)); 10 | return "woke up after " + seconds + " seconds"; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/getresultsasync/README.md: -------------------------------------------------------------------------------- 1 | # Get Workflow results async 2 | 3 | This sample shows the use of WorkflowStub.getResult and WorkflowStub.getResultAsync 4 | to show how the Temporal Client API can not only start Workflows async but also wait for their results 5 | async as well. 6 | 7 | ## Run the sample 8 | 9 | 1. Start the Worker: 10 | 11 | ```bash 12 | ./gradlew -q execute -PmainClass=io.temporal.samples.getresultsasync.Worker 13 | ``` 14 | 15 | 2. Start the Starter 16 | 17 | ```bash 18 | ./gradlew -q execute -PmainClass=io.temporal.samples.getresultsasync.Starter 19 | ``` -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/getresultsasync/Worker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.getresultsasync; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.WorkerFactory; 6 | 7 | public class Worker { 8 | public static final WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 9 | public static final WorkflowClient client = WorkflowClient.newInstance(service); 10 | public static final WorkerFactory factory = WorkerFactory.newInstance(client); 11 | public static final String TASK_QUEUE_NAME = "asyncstartqueue"; 12 | 13 | public static void main(String[] args) { 14 | io.temporal.worker.Worker worker = factory.newWorker(TASK_QUEUE_NAME); 15 | worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class); 16 | 17 | factory.start(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/Customer.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.listworkflows; 2 | 3 | public class Customer { 4 | private String accountNum; 5 | private String name; 6 | private String email; 7 | private String customerType; 8 | 9 | public Customer() {} 10 | 11 | public Customer(String accountNum, String name, String email, String customerType) { 12 | this.accountNum = accountNum; 13 | this.name = name; 14 | this.email = email; 15 | this.customerType = customerType; 16 | } 17 | 18 | public String getAccountNum() { 19 | return accountNum; 20 | } 21 | 22 | public void setAccountNum(String accountNum) { 23 | this.accountNum = accountNum; 24 | } 25 | 26 | public String getName() { 27 | return name; 28 | } 29 | 30 | public void setName(String name) { 31 | this.name = name; 32 | } 33 | 34 | public String getEmail() { 35 | return email; 36 | } 37 | 38 | public void setEmail(String email) { 39 | this.email = email; 40 | } 41 | 42 | public String getCustomerType() { 43 | return customerType; 44 | } 45 | 46 | public void setCustomerType(String customerType) { 47 | this.customerType = customerType; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/CustomerActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.listworkflows; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface CustomerActivities { 7 | void getCustomerAccount(Customer customer); 8 | 9 | void updateCustomerAccount(Customer customer, String message); 10 | 11 | void sendUpdateEmail(Customer customer); 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/CustomerActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.listworkflows; 2 | 3 | import java.util.concurrent.TimeUnit; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | public class CustomerActivitiesImpl implements CustomerActivities { 8 | 9 | private static final Logger log = LoggerFactory.getLogger(CustomerActivitiesImpl.class); 10 | 11 | @Override 12 | public void getCustomerAccount(Customer customer) { 13 | // simulate some actual work... 14 | sleepSeconds(1); 15 | } 16 | 17 | @Override 18 | public void updateCustomerAccount(Customer customer, String message) { 19 | // simulate some actual work... 20 | sleepSeconds(1); 21 | } 22 | 23 | @Override 24 | public void sendUpdateEmail(Customer customer) { 25 | // simulate some actual work... 26 | sleepSeconds(1); 27 | } 28 | 29 | private void sleepSeconds(int seconds) { 30 | try { 31 | Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); 32 | } catch (InterruptedException e) { 33 | // This is being swallowed on purpose 34 | Thread.currentThread().interrupt(); 35 | log.error("Exception in thread sleep: ", e); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/CustomerWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.listworkflows; 2 | 3 | import io.temporal.workflow.SignalMethod; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface CustomerWorkflow { 9 | @WorkflowMethod 10 | void updateAccountMessage(Customer customer, String message); 11 | 12 | @SignalMethod 13 | void exit(); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/CustomerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.listworkflows; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.common.RetryOptions; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | import java.util.Optional; 8 | 9 | public class CustomerWorkflowImpl implements CustomerWorkflow { 10 | private boolean exit; 11 | private final CustomerActivities customerActivities = 12 | Workflow.newActivityStub( 13 | CustomerActivities.class, 14 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 15 | private final RetryOptions customerRetryOptions = 16 | RetryOptions.newBuilder().setMaximumAttempts(5).build(); 17 | private final Duration expiration = Duration.ofMinutes(1); 18 | 19 | @Override 20 | public void updateAccountMessage(Customer customer, String message) { 21 | 22 | Workflow.retry( 23 | customerRetryOptions, 24 | Optional.of(expiration), 25 | () -> { 26 | customerActivities.getCustomerAccount(customer); 27 | customerActivities.updateCustomerAccount(customer, message); 28 | customerActivities.sendUpdateEmail(customer); 29 | }); 30 | 31 | Workflow.await(Duration.ofMinutes(1), () -> exit); 32 | } 33 | 34 | @Override 35 | public void exit() { 36 | this.exit = true; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/listworkflows/README.md: -------------------------------------------------------------------------------- 1 | # Demo List Workflows 2 | 3 | The sample demonstrates: 4 | 1) Setting custom search attributes for a Workflow 5 | 2) Using ListWorkflowExecutionsRequest and custom Search Attribute query to list 6 | Workflow Executions that match that query 7 | 8 | ## Running 9 | 10 | 1. Unlike the other examples, this one has to be started with Elasticsearch 11 | capabilities enabled. If you are using docker you can do that with: 12 | 13 | ```bash 14 | git clone https://github.com/temporalio/docker-compose.git 15 | cd docker-compose 16 | docker-compose -f docker-compose-cas-es.yml up 17 | ``` 18 | 19 | 2. 20 | Run the following command to start the sample: 21 | 22 | ```bash 23 | ./gradlew -q execute -PmainClass=io.temporal.samples.listworkflows.Starter 24 | ``` 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/metrics/MetricsUtils.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.metrics; 2 | 3 | import static java.nio.charset.StandardCharsets.UTF_8; 4 | 5 | import com.sun.net.httpserver.HttpServer; 6 | import io.micrometer.prometheus.PrometheusMeterRegistry; 7 | import java.io.IOException; 8 | import java.io.OutputStream; 9 | import java.net.InetSocketAddress; 10 | 11 | public class MetricsUtils { 12 | 13 | /** 14 | * Starts HttpServer to expose a scrape endpoint. See 15 | * https://micrometer.io/docs/registry/prometheus for more info. 16 | */ 17 | public static HttpServer startPrometheusScrapeEndpoint( 18 | PrometheusMeterRegistry registry, int port) { 19 | try { 20 | HttpServer server = HttpServer.create(new InetSocketAddress(port), 0); 21 | server.createContext( 22 | "/metrics", 23 | httpExchange -> { 24 | String response = registry.scrape(); 25 | httpExchange.sendResponseHeaders(200, response.getBytes(UTF_8).length); 26 | try (OutputStream os = httpExchange.getResponseBody()) { 27 | os.write(response.getBytes(UTF_8)); 28 | } 29 | }); 30 | 31 | server.start(); 32 | return server; 33 | } catch (IOException e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/metrics/README.md: -------------------------------------------------------------------------------- 1 | # Setting up SDK metrics (Prometheus) 2 | 3 | This sample shows setup for SDK metrics. 4 | 5 | 1. Start the Worker: 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.metrics.MetricsWorker 8 | ``` 9 | 10 | 2. Start the Starter: 11 | ```bash 12 | ./gradlew -q execute -PmainClass=io.temporal.samples.metrics.MetricsStarter 13 | ``` 14 | 15 | 3. See the worker metrics on the exposed Prometheus Scrape Endpoint: [http://localhost:8077/metrics](http://localhost:8077/metrics) 16 | 17 | 4. See the starter metrics on the exposed Prometheus Scrape Endpoint [http://localhost:8078/metrics](http://localhost:8078/metrics) 18 | 19 | 5. Stop the worker and starter 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/metrics/activities/MetricsActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.metrics.activities; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface MetricsActivities { 7 | String performA(String input); 8 | 9 | String performB(String input); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/metrics/workflow/MetricsWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.metrics.workflow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MetricsWorkflow { 8 | @WorkflowMethod 9 | String exec(String input); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/metrics/workflow/MetricsWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.metrics.workflow; 2 | 3 | import com.uber.m3.tally.Scope; 4 | import io.temporal.activity.ActivityOptions; 5 | import io.temporal.samples.metrics.activities.MetricsActivities; 6 | import io.temporal.workflow.Workflow; 7 | import java.time.Duration; 8 | import java.util.Collections; 9 | 10 | public class MetricsWorkflowImpl implements MetricsWorkflow { 11 | 12 | private final MetricsActivities activities = 13 | Workflow.newActivityStub( 14 | MetricsActivities.class, 15 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 16 | 17 | @Override 18 | public String exec(String input) { 19 | /* 20 | * Custom metric, we can use child scope and attach workflow_id as it's not attached by default 21 | * like task_queue ,workflow_type, etc 22 | */ 23 | Scope scope = 24 | Workflow.getMetricsScope() 25 | .tagged(Collections.singletonMap("workflow_id", Workflow.getInfo().getWorkflowId())); 26 | scope.counter("custom_metric").inc(1); 27 | 28 | String result = activities.performA(input); 29 | Workflow.sleep(Duration.ofSeconds(5)); 30 | result += activities.performB(input); 31 | 32 | return result; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneybatch/Account.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneybatch; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface Account { 7 | 8 | void deposit(String accountId, String referenceId, int amountCents); 9 | 10 | void withdraw(String accountId, String referenceId, int amountCents); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneybatch/AccountActivityWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneybatch; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | public class AccountActivityWorker { 9 | 10 | static final String TASK_QUEUE = "Account"; 11 | 12 | @SuppressWarnings("CatchAndPrintStackTrace") 13 | public static void main(String[] args) { 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | WorkflowClient client = WorkflowClient.newInstance(service); 16 | 17 | WorkerFactory factory = WorkerFactory.newInstance(client); 18 | Worker worker = factory.newWorker(TASK_QUEUE); 19 | 20 | Account account = new AccountImpl(); 21 | worker.registerActivitiesImplementations(account); 22 | 23 | factory.start(); 24 | System.out.println("Activity Worker started for task queue: " + TASK_QUEUE); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneybatch/AccountImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneybatch; 2 | 3 | public class AccountImpl implements Account { 4 | @Override 5 | public void deposit(String accountId, String referenceId, int amountCents) { 6 | System.out.printf( 7 | "Deposit to %s of %d cents requested. ReferenceId=%s\n", 8 | accountId, amountCents, referenceId); 9 | // throw new RuntimeException("simulated"); // Uncomment to simulate failure 10 | } 11 | 12 | @Override 13 | public void withdraw(String accountId, String referenceId, int amountCents) { 14 | System.out.printf( 15 | "Withdraw to %s of %d cents requested. ReferenceId=%s\n", 16 | accountId, amountCents, referenceId); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneybatch/AccountTransferWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneybatch; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | public class AccountTransferWorker { 9 | 10 | @SuppressWarnings("CatchAndPrintStackTrace") 11 | public static void main(String[] args) { 12 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 13 | WorkflowClient client = WorkflowClient.newInstance(service); 14 | WorkerFactory factory = WorkerFactory.newInstance(client); 15 | 16 | Worker worker = factory.newWorker(AccountActivityWorker.TASK_QUEUE); 17 | worker.registerWorkflowImplementationTypes(AccountTransferWorkflowImpl.class); 18 | 19 | factory.start(); 20 | System.out.println("Worker started for task queue: " + AccountActivityWorker.TASK_QUEUE); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneybatch/AccountTransferWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneybatch; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | import io.temporal.workflow.WorkflowInterface; 6 | import io.temporal.workflow.WorkflowMethod; 7 | 8 | @WorkflowInterface 9 | public interface AccountTransferWorkflow { 10 | 11 | @WorkflowMethod 12 | void deposit(String toAccountId, int batchSize); 13 | 14 | @SignalMethod 15 | void withdraw(String fromAccountId, String referenceId, int amountCents); 16 | 17 | @QueryMethod 18 | int getBalance(); 19 | 20 | @QueryMethod 21 | int getCount(); 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/Account.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface Account { 7 | 8 | void deposit(String accountId, String referenceId, int amountCents); 9 | 10 | void withdraw(String accountId, String referenceId, int amountCents); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/AccountActivityWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | public class AccountActivityWorker { 9 | 10 | public static final String TASK_QUEUE = "AccountTransfer"; 11 | 12 | @SuppressWarnings("CatchAndPrintStackTrace") 13 | public static void main(String[] args) { 14 | // gRPC stubs wrapper that talks to the local docker instance of temporal service. 15 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 16 | // client that can be used to start and signal workflows 17 | WorkflowClient client = WorkflowClient.newInstance(service); 18 | 19 | // worker factory that can be used to create workers for specific task queues 20 | WorkerFactory factory = WorkerFactory.newInstance(client); 21 | Worker worker = factory.newWorker(TASK_QUEUE); 22 | Account account = new AccountImpl(); 23 | worker.registerActivitiesImplementations(account); 24 | 25 | // Start all workers created by this factory. 26 | factory.start(); 27 | System.out.println("Activity Worker started for task queue: " + TASK_QUEUE); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/AccountImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | public class AccountImpl implements Account { 4 | 5 | @Override 6 | public void withdraw(String accountId, String referenceId, int amountCents) { 7 | System.out.printf( 8 | "Withdraw to %s of %d cents requested. ReferenceId=%s\n", 9 | accountId, amountCents, referenceId); 10 | } 11 | 12 | @Override 13 | public void deposit(String accountId, String referenceId, int amountCents) { 14 | System.out.printf( 15 | "Deposit to %s of %d cents requested. ReferenceId=%s\n", 16 | accountId, amountCents, referenceId); 17 | // throw new RuntimeException("simulated"); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | import static io.temporal.samples.moneytransfer.AccountActivityWorker.TASK_QUEUE; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | import io.temporal.worker.Worker; 8 | import io.temporal.worker.WorkerFactory; 9 | 10 | public class AccountTransferWorker { 11 | 12 | @SuppressWarnings("CatchAndPrintStackTrace") 13 | public static void main(String[] args) { 14 | // Get worker to poll the common task queue. 15 | // gRPC stubs wrapper that talks to the local docker instance of temporal service. 16 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 17 | // client that can be used to start and signal workflows 18 | WorkflowClient client = WorkflowClient.newInstance(service); 19 | 20 | // worker factory that can be used to create workers for specific task queues 21 | WorkerFactory factory = WorkerFactory.newInstance(client); 22 | Worker worker = factory.newWorker(TASK_QUEUE); 23 | worker.registerWorkflowImplementationTypes(AccountTransferWorkflowImpl.class); 24 | // Start all workers created by this factory. 25 | factory.start(); 26 | System.out.println("Worker started for task queue: " + TASK_QUEUE); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface AccountTransferWorkflow { 8 | @WorkflowMethod 9 | void transfer(String fromAccountId, String toAccountId, String referenceId, int amountCents); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.moneytransfer; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.workflow.Workflow; 5 | import java.time.Duration; 6 | 7 | public class AccountTransferWorkflowImpl implements AccountTransferWorkflow { 8 | 9 | private final ActivityOptions options = 10 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build(); 11 | private final Account account = Workflow.newActivityStub(Account.class, options); 12 | 13 | @Override 14 | public void transfer( 15 | String fromAccountId, String toAccountId, String referenceId, int amountCents) { 16 | account.withdraw(fromAccountId, referenceId, amountCents); 17 | account.deposit(toAccountId, referenceId, amountCents); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/moneytransfer/README.MD: -------------------------------------------------------------------------------- 1 | The Money Transfer sample has three separate processes. 2 | One to host Workflow Executions, another to host Activity Executions, and the third one to request transfers (start Workflow Executions). 3 | 4 | Start Workflow Worker: 5 | 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.AccountTransferWorker 8 | ``` 9 | 10 | Start Activity Worker: 11 | 12 | ```bash 13 | ./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.AccountActivityWorker 14 | ``` 15 | 16 | Execute once per requested transfer: 17 | 18 | ```bash 19 | ./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.TransferRequester 20 | ``` 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.caller; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.samples.nexus.options.ClientOptions; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | import io.temporal.worker.WorkflowImplementationOptions; 8 | import io.temporal.workflow.NexusServiceOptions; 9 | import java.util.Collections; 10 | 11 | public class CallerWorker { 12 | public static final String DEFAULT_TASK_QUEUE_NAME = "my-caller-workflow-task-queue"; 13 | 14 | public static void main(String[] args) { 15 | WorkflowClient client = ClientOptions.getWorkflowClient(args); 16 | 17 | WorkerFactory factory = WorkerFactory.newInstance(client); 18 | 19 | Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); 20 | worker.registerWorkflowImplementationTypes( 21 | WorkflowImplementationOptions.newBuilder() 22 | .setNexusServiceOptions( 23 | Collections.singletonMap( 24 | "NexusService", 25 | NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) 26 | .build(), 27 | EchoCallerWorkflowImpl.class, 28 | HelloCallerWorkflowImpl.class); 29 | 30 | factory.start(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.caller; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface EchoCallerWorkflow { 8 | @WorkflowMethod 9 | String echo(String message); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.caller; 2 | 3 | import io.temporal.samples.nexus.service.NexusService; 4 | import io.temporal.workflow.NexusOperationOptions; 5 | import io.temporal.workflow.NexusServiceOptions; 6 | import io.temporal.workflow.Workflow; 7 | import java.time.Duration; 8 | 9 | public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { 10 | NexusService nexusService = 11 | Workflow.newNexusServiceStub( 12 | NexusService.class, 13 | NexusServiceOptions.newBuilder() 14 | .setOperationOptions( 15 | NexusOperationOptions.newBuilder() 16 | .setScheduleToCloseTimeout(Duration.ofSeconds(10)) 17 | .build()) 18 | .build()); 19 | 20 | @Override 21 | public String echo(String message) { 22 | return nexusService.echo(new NexusService.EchoInput(message)).getMessage(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.caller; 2 | 3 | import io.temporal.samples.nexus.service.NexusService; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface HelloCallerWorkflow { 9 | @WorkflowMethod 10 | String hello(String message, NexusService.Language language); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.caller; 2 | 3 | import io.temporal.samples.nexus.service.NexusService; 4 | import io.temporal.workflow.NexusOperationHandle; 5 | import io.temporal.workflow.NexusOperationOptions; 6 | import io.temporal.workflow.NexusServiceOptions; 7 | import io.temporal.workflow.Workflow; 8 | import java.time.Duration; 9 | 10 | public class HelloCallerWorkflowImpl implements HelloCallerWorkflow { 11 | NexusService nexusService = 12 | Workflow.newNexusServiceStub( 13 | NexusService.class, 14 | NexusServiceOptions.newBuilder() 15 | .setOperationOptions( 16 | NexusOperationOptions.newBuilder() 17 | .setScheduleToCloseTimeout(Duration.ofSeconds(10)) 18 | .build()) 19 | .build()); 20 | 21 | @Override 22 | public String hello(String message, NexusService.Language language) { 23 | NexusOperationHandle handle = 24 | Workflow.startNexusOperation( 25 | nexusService::hello, new NexusService.HelloInput(message, language)); 26 | // Optionally wait for the operation to be started. NexusOperationExecution will contain the 27 | // operation token in case this operation is asynchronous. 28 | handle.getExecution().get(); 29 | return handle.getResult().get().getMessage(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.handler; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.samples.nexus.options.ClientOptions; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | 8 | public class HandlerWorker { 9 | public static final String DEFAULT_TASK_QUEUE_NAME = "my-handler-task-queue"; 10 | 11 | public static void main(String[] args) { 12 | WorkflowClient client = ClientOptions.getWorkflowClient(args); 13 | 14 | WorkerFactory factory = WorkerFactory.newInstance(client); 15 | 16 | Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); 17 | worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); 18 | worker.registerNexusServiceImplementation(new NexusServiceImpl()); 19 | 20 | factory.start(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.handler; 2 | 3 | import io.temporal.samples.nexus.service.NexusService; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface HelloHandlerWorkflow { 9 | @WorkflowMethod 10 | NexusService.HelloOutput hello(NexusService.HelloInput input); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexus.handler; 2 | 3 | import io.temporal.failure.ApplicationFailure; 4 | import io.temporal.samples.nexus.service.NexusService; 5 | 6 | public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { 7 | @Override 8 | public NexusService.HelloOutput hello(NexusService.HelloInput input) { 9 | switch (input.getLanguage()) { 10 | case EN: 11 | return new NexusService.HelloOutput("Hello " + input.getName() + " 👋"); 12 | case FR: 13 | return new NexusService.HelloOutput("Bonjour " + input.getName() + " 👋"); 14 | case DE: 15 | return new NexusService.HelloOutput("Hallo " + input.getName() + " 👋"); 16 | case ES: 17 | return new NexusService.HelloOutput("¡Hola! " + input.getName() + " 👋"); 18 | case TR: 19 | return new NexusService.HelloOutput("Merhaba " + input.getName() + " 👋"); 20 | } 21 | throw ApplicationFailure.newFailure( 22 | "Unsupported language: " + input.getLanguage(), "UNSUPPORTED_LANGUAGE"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexus/service/description.md: -------------------------------------------------------------------------------- 1 | ## Service: [NexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java) 2 | - operation: `echo` 3 | - operation: `hello` 4 | 5 | See https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/NexusService.java for Input / Output types. 6 | 7 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/README.MD: -------------------------------------------------------------------------------- 1 | # Nexus Cancellation 2 | 3 | This sample shows how to cancel a Nexus operation from a caller workflow. 4 | 5 | To run this sample, set up your environment following the instructions in the main [Nexus Sample](../nexus/README.md). 6 | 7 | Next, in separate terminal windows: 8 | 9 | ### Nexus handler worker 10 | 11 | ``` 12 | ./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.handler.HandlerWorker \ 13 | --args="-target-host localhost:7233 -namespace my-target-namespace" 14 | ``` 15 | 16 | ### Nexus caller worker 17 | 18 | ``` 19 | ./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.caller.CallerWorker \ 20 | --args="-target-host localhost:7233 -namespace my-caller-namespace" 21 | ``` 22 | 23 | ### Start caller workflow 24 | 25 | ``` 26 | ./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.caller.CallerStarter \ 27 | --args="-target-host localhost:7233 -namespace my-caller-namespace" 28 | ``` 29 | 30 | ### Output 31 | 32 | which should result in: 33 | ``` 34 | INFO i.t.s.n.caller.CallerStarter - Started workflow workflowId: 326732dd-a2b1-4de7-9ddd-dcee4f9f0229 runId: d580499f-79d5-461d-bd49-6248b4e522ae 35 | INFO i.t.s.n.caller.CallerStarter - Workflow result: Hallo Nexus 👋 36 | ``` 37 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerStarter.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscancellation.caller; 2 | 3 | import io.temporal.api.common.v1.WorkflowExecution; 4 | import io.temporal.client.WorkflowClient; 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.samples.nexus.options.ClientOptions; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class CallerStarter { 11 | private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class); 12 | 13 | public static void main(String[] args) { 14 | WorkflowClient client = ClientOptions.getWorkflowClient(args); 15 | 16 | WorkflowOptions workflowOptions = 17 | WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build(); 18 | HelloCallerWorkflow helloWorkflow = 19 | client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions); 20 | WorkflowExecution execution = WorkflowClient.start(helloWorkflow::hello, "Nexus"); 21 | logger.info( 22 | "Started workflow workflowId: {} runId: {}", 23 | execution.getWorkflowId(), 24 | execution.getRunId()); 25 | logger.info("Workflow result: {}", helloWorkflow.hello("Nexus")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscancellation.caller; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.samples.nexus.options.ClientOptions; 5 | import io.temporal.samples.nexus.service.NexusService; 6 | import io.temporal.worker.Worker; 7 | import io.temporal.worker.WorkerFactory; 8 | import io.temporal.worker.WorkflowImplementationOptions; 9 | import io.temporal.workflow.NexusServiceOptions; 10 | import java.util.Collections; 11 | 12 | public class CallerWorker { 13 | public static final String DEFAULT_TASK_QUEUE_NAME = "my-caller-workflow-task-queue"; 14 | 15 | public static void main(String[] args) { 16 | WorkflowClient client = ClientOptions.getWorkflowClient(args); 17 | 18 | WorkerFactory factory = WorkerFactory.newInstance(client); 19 | 20 | Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); 21 | worker.registerWorkflowImplementationTypes( 22 | WorkflowImplementationOptions.newBuilder() 23 | .setNexusServiceOptions( 24 | Collections.singletonMap( 25 | NexusService.class.getSimpleName(), 26 | NexusServiceOptions.newBuilder().setEndpoint("my-nexus-endpoint-name").build())) 27 | .build(), 28 | HelloCallerWorkflowImpl.class); 29 | 30 | factory.start(); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscancellation.caller; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface HelloCallerWorkflow { 8 | @WorkflowMethod 9 | String hello(String message); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscancellation.handler; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.samples.nexus.handler.NexusServiceImpl; 5 | import io.temporal.samples.nexus.options.ClientOptions; 6 | import io.temporal.worker.Worker; 7 | import io.temporal.worker.WorkerFactory; 8 | 9 | public class HandlerWorker { 10 | public static final String DEFAULT_TASK_QUEUE_NAME = "my-handler-task-queue"; 11 | 12 | public static void main(String[] args) { 13 | WorkflowClient client = ClientOptions.getWorkflowClient(args); 14 | 15 | WorkerFactory factory = WorkerFactory.newInstance(client); 16 | 17 | Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME); 18 | worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class); 19 | worker.registerNexusServiceImplementation(new NexusServiceImpl()); 20 | 21 | factory.start(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscancellation.handler; 2 | 3 | import io.temporal.failure.ApplicationFailure; 4 | import io.temporal.samples.nexus.handler.HelloHandlerWorkflow; 5 | import io.temporal.samples.nexus.service.NexusService; 6 | import io.temporal.workflow.Workflow; 7 | import java.time.Duration; 8 | 9 | public class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow { 10 | @Override 11 | public NexusService.HelloOutput hello(NexusService.HelloInput input) { 12 | // Sleep for a random duration to simulate some work 13 | Workflow.sleep(Duration.ofSeconds(Workflow.newRandom().nextInt(5))); 14 | switch (input.getLanguage()) { 15 | case EN: 16 | return new NexusService.HelloOutput("Hello " + input.getName() + " 👋"); 17 | case FR: 18 | return new NexusService.HelloOutput("Bonjour " + input.getName() + " 👋"); 19 | case DE: 20 | return new NexusService.HelloOutput("Hallo " + input.getName() + " 👋"); 21 | case ES: 22 | return new NexusService.HelloOutput("¡Hola! " + input.getName() + " 👋"); 23 | case TR: 24 | return new NexusService.HelloOutput("Merhaba " + input.getName() + " 👋"); 25 | } 26 | throw ApplicationFailure.newFailure( 27 | "Unsupported language: " + input.getLanguage(), "UNSUPPORTED_LANGUAGE"); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.nexuscontextpropagation.caller; 2 | 3 | import io.temporal.samples.nexus.caller.EchoCallerWorkflow; 4 | import io.temporal.samples.nexus.service.NexusService; 5 | import io.temporal.workflow.NexusOperationOptions; 6 | import io.temporal.workflow.NexusServiceOptions; 7 | import io.temporal.workflow.Workflow; 8 | import java.time.Duration; 9 | import org.slf4j.MDC; 10 | 11 | public class EchoCallerWorkflowImpl implements EchoCallerWorkflow { 12 | NexusService nexusService = 13 | Workflow.newNexusServiceStub( 14 | NexusService.class, 15 | NexusServiceOptions.newBuilder() 16 | .setOperationOptions( 17 | NexusOperationOptions.newBuilder() 18 | .setScheduleToCloseTimeout(Duration.ofSeconds(10)) 19 | .build()) 20 | .build()); 21 | 22 | @Override 23 | public String echo(String message) { 24 | MDC.put("x-nexus-caller-workflow-id", Workflow.getInfo().getWorkflowId()); 25 | return nexusService.echo(new NexusService.EchoInput(message)).getMessage(); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/CEWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.payloadconverter.cloudevents; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import io.temporal.workflow.QueryMethod; 5 | import io.temporal.workflow.SignalMethod; 6 | import io.temporal.workflow.WorkflowInterface; 7 | import io.temporal.workflow.WorkflowMethod; 8 | 9 | @WorkflowInterface 10 | public interface CEWorkflow { 11 | @WorkflowMethod 12 | void exec(CloudEvent cloudEvent); 13 | 14 | @SignalMethod 15 | void addEvent(CloudEvent cloudEvent); 16 | 17 | @QueryMethod 18 | CloudEvent getLastEvent(); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/CEWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.payloadconverter.cloudevents; 2 | 3 | import io.cloudevents.CloudEvent; 4 | import io.temporal.workflow.Workflow; 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class CEWorkflowImpl implements CEWorkflow { 9 | 10 | private List eventList = new ArrayList<>(); 11 | 12 | @Override 13 | public void exec(CloudEvent cloudEvent) { 14 | 15 | eventList.add(cloudEvent); 16 | 17 | Workflow.await(() -> eventList.size() == 10); 18 | } 19 | 20 | @Override 21 | public void addEvent(CloudEvent cloudEvent) { 22 | eventList.add(cloudEvent); 23 | } 24 | 25 | @Override 26 | public CloudEvent getLastEvent() { 27 | return eventList.get(eventList.size() - 1); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/README.md: -------------------------------------------------------------------------------- 1 | # Custom Payload Converter (CloudEvents) 2 | 3 | The sample demonstrates creating and setting a custom Payload Converter. 4 | 5 | ## Running 6 | 7 | 1. Start Temporal Server with "default" namespace enabled. 8 | For example using local Docker: 9 | 10 | ```bash 11 | git clone https://github.com/temporalio/docker-compose.git 12 | cd docker-compose 13 | docker-compose up 14 | ``` 15 | 16 | 2. Run the following command to start the sample: 17 | 18 | ```bash 19 | ./gradlew -q execute -PmainClass=io.temporal.samples.payloadconverter.cloudevents.Starter 20 | ``` 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/crypto/CryptoWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.payloadconverter.crypto; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface CryptoWorkflow { 8 | @WorkflowMethod 9 | MyCustomer exec(MyCustomer customer); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/crypto/CryptoWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.payloadconverter.crypto; 2 | 3 | public class CryptoWorkflowImpl implements CryptoWorkflow { 4 | @Override 5 | public MyCustomer exec(MyCustomer customer) { 6 | // if > 18 "approve" otherwise deny 7 | if (customer.getAge() > 18) { 8 | customer.setApproved(true); 9 | } else { 10 | customer.setApproved(false); 11 | } 12 | return customer; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/payloadconverter/crypto/MyCustomer.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.payloadconverter.crypto; 2 | 3 | import com.codingrodent.jackson.crypto.Encrypt; 4 | 5 | public class MyCustomer { 6 | private String name; 7 | private int age; 8 | private boolean approved; 9 | 10 | public MyCustomer() {} 11 | 12 | public MyCustomer(String name, int age) { 13 | this.name = name; 14 | this.age = age; 15 | } 16 | 17 | @Encrypt 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public void setName(String name) { 23 | this.name = name; 24 | } 25 | 26 | @Encrypt 27 | public int getAge() { 28 | return age; 29 | } 30 | 31 | public void setAge(int age) { 32 | this.age = age; 33 | } 34 | 35 | @Encrypt 36 | public boolean isApproved() { 37 | return approved; 38 | } 39 | 40 | public void setApproved(boolean approved) { 41 | this.approved = approved; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/peractivityoptions/FailingActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.peractivityoptions; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface FailingActivities { 7 | void activityTypeA(); 8 | 9 | void activityTypeB(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/peractivityoptions/FailingActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.peractivityoptions; 2 | 3 | import io.temporal.activity.Activity; 4 | 5 | public class FailingActivitiesImpl implements FailingActivities { 6 | @Override 7 | public void activityTypeA() { 8 | // Get the activity type 9 | String type = Activity.getExecutionContext().getInfo().getActivityType(); 10 | // Get the retry attempt 11 | int attempt = Activity.getExecutionContext().getInfo().getAttempt(); 12 | // Wrap checked exception and throw 13 | throw Activity.wrap( 14 | new NullPointerException("Activity type: " + type + " attempt times: " + attempt)); 15 | } 16 | 17 | @Override 18 | public void activityTypeB() { 19 | // Get the activity type 20 | String type = Activity.getExecutionContext().getInfo().getActivityType(); 21 | // Get the retry attempt 22 | int attempt = Activity.getExecutionContext().getInfo().getAttempt(); 23 | // Wrap checked exception and throw 24 | throw Activity.wrap( 25 | new NullPointerException("Activity type: " + type + " attempt times: " + attempt)); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/peractivityoptions/PerActivityOptionsWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.peractivityoptions; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface PerActivityOptionsWorkflow { 8 | @WorkflowMethod 9 | void execute(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/peractivityoptions/README.md: -------------------------------------------------------------------------------- 1 | # Per Activity Type Options Sample 2 | 3 | This sample shows how to set per Activity type options via 4 | WorkflowImplementationOptions 5 | 6 | ```bash 7 | ./gradlew -q execute -PmainClass=io.temporal.samples.peractivityoptions.Starter 8 | ``` 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/PollingActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface PollingActivities { 7 | String doPoll(); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/PollingWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface PollingWorkflow { 8 | @WorkflowMethod 9 | String exec(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/README.md: -------------------------------------------------------------------------------- 1 | # Polling 2 | 3 | These samples show three different best practices for polling. 4 | 5 | 1. [Frequently Polling Activity](frequent/README.md) 6 | 2. [Infrequently Polling Activity](infrequent/README.md) 7 | 3. [Periodic Polling of a sequence of activities](periodicsequence/README.md) 8 | 9 | The samples are based on [this](https://community.temporal.io/t/what-is-the-best-practice-for-a-polling-activity/328/2) community forum thread. -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/frequent/README.md: -------------------------------------------------------------------------------- 1 | ## Frequent polling 2 | 3 | This sample shows how we can implement frequent polling (1 second or faster) inside our Activity. 4 | The implementation is a loop that polls our service and then sleeps for the poll interval (1 second in the sample). 5 | 6 | To ensure that polling activity is restarted in a timely manner, we make sure that it heartbeats on every iteration. 7 | Note that heartbeating only works if we set the HeartBeatTimeout to a shorter value than the activity 8 | StartToClose timeout. 9 | 10 | 11 | To run this sample: 12 | ```bash 13 | ./gradlew -q execute -PmainClass=io.temporal.samples.polling.frequent.FrequentPollingStarter 14 | ``` -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/infrequent/InfrequentPollingActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling.infrequent; 2 | 3 | import io.temporal.activity.Activity; 4 | import io.temporal.samples.polling.PollingActivities; 5 | import io.temporal.samples.polling.TestService; 6 | 7 | public class InfrequentPollingActivityImpl implements PollingActivities { 8 | private TestService service; 9 | 10 | public InfrequentPollingActivityImpl(TestService service) { 11 | this.service = service; 12 | } 13 | 14 | @Override 15 | public String doPoll() { 16 | try { 17 | return service.getServiceResult(); 18 | } catch (TestService.TestServiceException e) { 19 | // We want to rethrow the service exception so we can poll via activity retries 20 | throw Activity.wrap(e); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/infrequent/README.md: -------------------------------------------------------------------------------- 1 | ## Infrequent polling 2 | 3 | This sample shows how we can use Activity retries for infrequent polling of a third-party service (for example via REST). 4 | This method can be used for infrequent polls of one minute or slower. 5 | 6 | We utilize activity retries for this option, setting Retries options: 7 | * setBackoffCoefficient to 1 8 | * setInitialInterval to the polling interval (in this sample set to 60 seconds) 9 | 10 | This will allow us to retry our Activity exactly on the set interval. 11 | 12 | To run this sample: 13 | ```bash 14 | ./gradlew -q execute -PmainClass=io.temporal.samples.polling.infrequent.InfrequentPollingStarter 15 | ``` 16 | 17 | Since our test service simulates it being "down" for 4 polling attempts and then returns "OK" on the 5th poll attempt, our Workflow is going to perform 4 activity retries with a 60 second poll interval, and then return the service result on the successful 5th attempt. 18 | 19 | Note that individual Activity retries are not recorded in Workflow History, so we this approach we can poll for a very long time without affecting the history size. 20 | 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling.periodicsequence; 2 | 3 | import io.temporal.activity.Activity; 4 | import io.temporal.samples.polling.PollingActivities; 5 | import io.temporal.samples.polling.TestService; 6 | 7 | public class PeriodicPollingActivityImpl implements PollingActivities { 8 | 9 | private TestService service; 10 | 11 | public PeriodicPollingActivityImpl(TestService service) { 12 | this.service = service; 13 | } 14 | 15 | @Override 16 | public String doPoll() { 17 | try { 18 | return service.getServiceResult(); 19 | } catch (TestService.TestServiceException e) { 20 | // We want to rethrow the service exception so we can poll via activity retries 21 | throw Activity.wrap(e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling.periodicsequence; 2 | 3 | import io.temporal.samples.polling.PollingWorkflow; 4 | import io.temporal.workflow.ChildWorkflowOptions; 5 | import io.temporal.workflow.Workflow; 6 | 7 | public class PeriodicPollingWorkflowImpl implements PollingWorkflow { 8 | 9 | // Set some periodic poll interval, for sample we set 5 seconds 10 | private int pollingIntervalInSeconds = 5; 11 | 12 | @Override 13 | public String exec() { 14 | PollingChildWorkflow childWorkflow = 15 | Workflow.newChildWorkflowStub( 16 | PollingChildWorkflow.class, 17 | ChildWorkflowOptions.newBuilder().setWorkflowId("ChildWorkflowPoll").build()); 18 | 19 | return childWorkflow.exec(pollingIntervalInSeconds); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/periodicsequence/PollingChildWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling.periodicsequence; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface PollingChildWorkflow { 8 | @WorkflowMethod 9 | String exec(int pollingIntervalInSeconds); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/polling/periodicsequence/README.md: -------------------------------------------------------------------------------- 1 | ## Periodic sequence 2 | 3 | This samples shows periodic polling via Child Workflow. 4 | 5 | This is a rare scenario where polling requires execution of a sequence of Activities, or Activity arguments need to change between polling retries. 6 | 7 | For this case we use a Child Workflow to call polling Activities a set number of times in a loop and then periodically calls continue-as-new. 8 | 9 | The Parent Workflow is not aware about the Child Workflow calling continue-as-new and it gets notified when it completes (or fails). 10 | 11 | To run this sample: 12 | ```bash 13 | ./gradlew -q execute -PmainClass=io.temporal.samples.polling.periodicsequence.PeriodicPollingStarter 14 | ``` 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/FailureRequester.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | 8 | /** 9 | * Send signal requesting that an exception thrown from the activity is propagated to the workflow. 10 | */ 11 | public class FailureRequester { 12 | 13 | public static void main(String[] args) { 14 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 15 | WorkflowClient client = WorkflowClient.newInstance(service); 16 | 17 | // Note that we use the listener interface that the interceptor registered dynamically, not the 18 | // workflow interface. 19 | RetryOnSignalInterceptorListener workflow = 20 | client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID); 21 | 22 | // Sends "Fail" signal to the workflow. 23 | workflow.fail(); 24 | 25 | System.out.println("\"Fail\" signal sent"); 26 | System.exit(0); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface MyActivity { 7 | void execute(); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.failure.ApplicationFailure; 4 | import java.util.concurrent.atomic.AtomicInteger; 5 | 6 | public class MyActivityImpl implements MyActivity { 7 | 8 | /** 9 | * WARNING! Never rely on such shared state in real applications. The activity variables are per 10 | * process and in almost all cases multiple worker processes are used. 11 | */ 12 | private final AtomicInteger count = new AtomicInteger(); 13 | 14 | /** Sleeps 5 seconds. Fails for 4 first invocations, and then completes. */ 15 | @Override 16 | public void execute() { 17 | try { 18 | Thread.sleep(5000); 19 | } catch (InterruptedException e) { 20 | throw new RuntimeException(e); 21 | } 22 | if (count.incrementAndGet() < 5) { 23 | throw ApplicationFailure.newFailure("simulated", "type1"); 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyWorkflow { 8 | 9 | @WorkflowMethod 10 | void execute(); 11 | } 12 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.common.RetryOptions; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | public class MyWorkflowImpl implements MyWorkflow { 9 | 10 | private final MyActivity activity = 11 | Workflow.newActivityStub( 12 | MyActivity.class, 13 | ActivityOptions.newBuilder() 14 | .setStartToCloseTimeout(Duration.ofSeconds(30)) 15 | // disable server side retries. In most production applications the retries should be 16 | // done for a while before requiring an external operator signal. 17 | .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build()) 18 | .build()); 19 | 20 | @Override 21 | public void execute() { 22 | activity.execute(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/QueryRequester.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | 8 | public class QueryRequester { 9 | 10 | public static void main(String[] args) { 11 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 12 | WorkflowClient client = WorkflowClient.newInstance(service); 13 | 14 | // Note that we use the listener interface that the interceptor registered dynamically, not the 15 | // workflow interface. 16 | RetryOnSignalInterceptorListener workflow = 17 | client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID); 18 | 19 | // Queries workflow. 20 | String status = workflow.getPendingActivitiesStatus(); 21 | 22 | System.out.println("Workflow Pending Activities Status:\n\n" + status); 23 | System.exit(0); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/README.MD: -------------------------------------------------------------------------------- 1 | # The Retry On Signal Interceptor 2 | 3 | Demonstrates an interceptor that upon activity failure waits for an external signal that indicates if activity should 4 | fail or retry. 5 | 6 | Starts Worker. The worker upon start initiates a workflow that has an activity that fails on the fist invocation. 7 | 8 | ```bash 9 | ./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker 10 | ``` 11 | 12 | Sends Signal to indicate that the activity should be retried. 13 | 14 | ```bash 15 | ./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.RetryRequester 16 | ``` 17 | 18 | Sends a signal to propagate the activity failure to the workflow instead of retrying. 19 | 20 | ```bash 21 | ./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.FailureRequester 22 | ``` 23 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalInterceptorListener.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | 6 | /** Interface used to dynamically register signal and query handlers from the interceptor. */ 7 | public interface RetryOnSignalInterceptorListener { 8 | 9 | /** Requests retry of the activities waiting after failure. */ 10 | @SignalMethod 11 | void retry(); 12 | 13 | /** Requests no more retries of the activities waiting after failure. */ 14 | @SignalMethod 15 | void fail(); 16 | 17 | /** Returns human status of the pending activities. */ 18 | @QueryMethod 19 | String getPendingActivitiesStatus(); 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalWorkerInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.common.interceptors.*; 4 | 5 | /** Should be registered through WorkerFactoryOptions. */ 6 | public class RetryOnSignalWorkerInterceptor extends WorkerInterceptorBase { 7 | @Override 8 | public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) { 9 | return new RetryOnSignalWorkflowInboundCallsInterceptor(next); 10 | } 11 | 12 | @Override 13 | public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) { 14 | return next; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalWorkflowInboundCallsInterceptor.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import io.temporal.common.interceptors.WorkflowInboundCallsInterceptor; 4 | import io.temporal.common.interceptors.WorkflowInboundCallsInterceptorBase; 5 | import io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor; 6 | 7 | public class RetryOnSignalWorkflowInboundCallsInterceptor 8 | extends WorkflowInboundCallsInterceptorBase { 9 | 10 | public RetryOnSignalWorkflowInboundCallsInterceptor(WorkflowInboundCallsInterceptor next) { 11 | super(next); 12 | } 13 | 14 | @Override 15 | public void init(WorkflowOutboundCallsInterceptor outboundCalls) { 16 | super.init(new RetryOnSignalWorkflowOutboundCallsInterceptor(outboundCalls)); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryRequester.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.retryonsignalinterceptor; 2 | 3 | import static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | 8 | public class RetryRequester { 9 | 10 | public static void main(String[] args) { 11 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 12 | WorkflowClient client = WorkflowClient.newInstance(service); 13 | 14 | // Note that we use the listener interface that the interceptor registered dynamically, not the 15 | // workflow interface. 16 | RetryOnSignalInterceptorListener workflow = 17 | client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID); 18 | 19 | // Sends "Retry" signal to the workflow. 20 | workflow.retry(); 21 | 22 | System.out.println("\"Retry\" signal sent"); 23 | System.exit(0); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflowWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.safemessagepassing; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class ClusterManagerWorkflowWorker { 11 | private static final Logger logger = LoggerFactory.getLogger(ClusterManagerWorkflowWorker.class); 12 | static final String TASK_QUEUE = "ClusterManagerWorkflowTaskQueue"; 13 | static final String CLUSTER_MANAGER_WORKFLOW_ID = "ClusterManagerWorkflow"; 14 | 15 | public static void main(String[] args) { 16 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 17 | WorkflowClient client = WorkflowClient.newInstance(service); 18 | WorkerFactory factory = WorkerFactory.newInstance(client); 19 | final Worker worker = factory.newWorker(TASK_QUEUE); 20 | worker.registerWorkflowImplementationTypes(ClusterManagerWorkflowImpl.class); 21 | worker.registerActivitiesImplementations(new ClusterManagerActivitiesImpl()); 22 | factory.start(); 23 | logger.info("Worker started for task queue: " + TASK_QUEUE); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/README.md: -------------------------------------------------------------------------------- 1 | # Sleep for days 2 | 3 | This sample demonstrates how to use Temporal to run a workflow that periodically sleeps for a number of days. 4 | 5 | ## Run the sample 6 | 7 | 1. Start the Worker: 8 | 9 | ```bash 10 | ./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Worker 11 | ``` 12 | 13 | 2. Start the Starter 14 | 15 | ```bash 16 | ./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Starter 17 | ``` -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface SendEmailActivity { 7 | void sendEmail(String email); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | public class SendEmailActivityImpl implements SendEmailActivity { 4 | @Override 5 | public void sendEmail(String email) { 6 | System.out.println(email); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.workflow.Promise; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | public class SleepForDaysImpl implements SleepForDaysWorkflow { 9 | 10 | private final SendEmailActivity activity; 11 | private boolean complete = false; 12 | 13 | public SleepForDaysImpl() { 14 | this.activity = 15 | Workflow.newActivityStub( 16 | SendEmailActivity.class, 17 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build()); 18 | } 19 | 20 | @Override 21 | public String sleepForDays() { 22 | while (!this.complete) { 23 | activity.sendEmail(String.format("Sleeping for 30 days")); 24 | Promise timer = Workflow.newTimer(Duration.ofDays(30)); 25 | Workflow.await(() -> timer.isCompleted() || this.complete); 26 | } 27 | 28 | return "done!"; 29 | } 30 | 31 | @Override 32 | public void complete() { 33 | this.complete = true; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | import io.temporal.workflow.SignalMethod; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface SleepForDaysWorkflow { 9 | @WorkflowMethod 10 | String sleepForDays(); 11 | 12 | @SignalMethod 13 | void complete(); 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/Starter.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.client.WorkflowOptions; 5 | import io.temporal.client.WorkflowStub; 6 | 7 | public class Starter { 8 | 9 | public static final String TASK_QUEUE = "SleepForDaysTaskQueue"; 10 | 11 | public static void main(String[] args) { 12 | // Start a workflow execution. 13 | SleepForDaysWorkflow workflow = 14 | Worker.client.newWorkflowStub( 15 | SleepForDaysWorkflow.class, 16 | WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build()); 17 | 18 | // Start the workflow. 19 | WorkflowClient.start(workflow::sleepForDays); 20 | 21 | WorkflowStub stub = WorkflowStub.fromTyped(workflow); 22 | 23 | // Wait for workflow to complete. This will wait indefinitely until a 'complete' signal is sent. 24 | stub.getResult(String.class); 25 | System.exit(0); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/sleepfordays/Worker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.sleepfordays; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.WorkerFactory; 6 | 7 | public class Worker { 8 | public static final String TASK_QUEUE = "SleepForDaysTaskQueue"; 9 | public static final WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 10 | public static final WorkflowClient client = WorkflowClient.newInstance(service); 11 | public static final WorkerFactory factory = WorkerFactory.newInstance(client); 12 | 13 | public static void main(String[] args) { 14 | io.temporal.worker.Worker worker = factory.newWorker(TASK_QUEUE); 15 | worker.registerWorkflowImplementationTypes(SleepForDaysImpl.class); 16 | worker.registerActivitiesImplementations(new SendEmailActivityImpl()); 17 | 18 | factory.start(); 19 | System.out.println("Worker started for task queue: " + TASK_QUEUE); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/ssl/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.ssl; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyWorkflow { 8 | @WorkflowMethod 9 | String execute(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/ssl/MyWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.ssl; 2 | 3 | public class MyWorkflowImpl implements MyWorkflow { 4 | @Override 5 | public String execute() { 6 | return "done"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/terminateworkflow/MyWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.terminateworkflow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface MyWorkflow { 8 | @WorkflowMethod 9 | String execute(); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/terminateworkflow/MyWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.terminateworkflow; 2 | 3 | import io.temporal.workflow.Workflow; 4 | import java.time.Duration; 5 | 6 | public class MyWorkflowImpl implements MyWorkflow { 7 | @Override 8 | public String execute() { 9 | // This workflow just sleeps 10 | Workflow.sleep(Duration.ofSeconds(20)); 11 | return "done"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/terminateworkflow/README.md: -------------------------------------------------------------------------------- 1 | # Terminate Workflow execution 2 | 3 | The sample demonstrates how to terminate Workflow Execution using the Client API. 4 | 5 | ```bash 6 | ./gradlew -q execute -PmainClass=io.temporal.samples.terminateworkflow.Starter 7 | ``` 8 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface TracingActivities { 7 | String greet(String name, String language); 8 | } 9 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingActivitiesImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | public class TracingActivitiesImpl implements TracingActivities { 4 | @Override 5 | public String greet(String name, String language) { 6 | String greeting; 7 | 8 | switch (language) { 9 | case "Spanish": 10 | greeting = "Hola " + name; 11 | break; 12 | case "French": 13 | greeting = "Bonjour " + name; 14 | break; 15 | default: 16 | greeting = "Hello " + name; 17 | } 18 | 19 | return greeting; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingChildWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface TracingChildWorkflow { 8 | @WorkflowMethod 9 | String greet(String name, String language); 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingChildWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.workflow.Workflow; 5 | import java.time.Duration; 6 | 7 | public class TracingChildWorkflowImpl implements TracingChildWorkflow { 8 | @Override 9 | public String greet(String name, String language) { 10 | 11 | ActivityOptions activityOptions = 12 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build(); 13 | TracingActivities activities = 14 | Workflow.newActivityStub(TracingActivities.class, activityOptions); 15 | 16 | return activities.greet(name, language); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | import io.temporal.workflow.WorkflowInterface; 6 | import io.temporal.workflow.WorkflowMethod; 7 | 8 | @WorkflowInterface 9 | public interface TracingWorkflow { 10 | 11 | @WorkflowMethod 12 | String greet(String name); 13 | 14 | @SignalMethod 15 | void setLanguage(String language); 16 | 17 | @QueryMethod 18 | String getLanguage(); 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/tracing/workflow/TracingWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.tracing.workflow; 2 | 3 | import io.temporal.workflow.ChildWorkflowOptions; 4 | import io.temporal.workflow.Workflow; 5 | 6 | public class TracingWorkflowImpl implements TracingWorkflow { 7 | 8 | private String language = "English"; 9 | 10 | @Override 11 | public String greet(String name) { 12 | ChildWorkflowOptions options = 13 | ChildWorkflowOptions.newBuilder().setWorkflowId("tracingChildWorkflow").build(); 14 | 15 | // Get the child workflow stub 16 | TracingChildWorkflow child = Workflow.newChildWorkflowStub(TracingChildWorkflow.class, options); 17 | 18 | // Invoke child sync and return its result 19 | return child.greet(name, language); 20 | } 21 | 22 | @Override 23 | public void setLanguage(String language) { 24 | this.language = language; 25 | } 26 | 27 | @Override 28 | public String getLanguage() { 29 | return language; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.updatabletimer; 2 | 3 | import io.temporal.workflow.QueryMethod; 4 | import io.temporal.workflow.SignalMethod; 5 | import io.temporal.workflow.WorkflowInterface; 6 | import io.temporal.workflow.WorkflowMethod; 7 | 8 | @WorkflowInterface 9 | public interface DynamicSleepWorkflow { 10 | @WorkflowMethod 11 | void execute(long wakeUpTime); 12 | 13 | @SignalMethod 14 | void updateWakeUpTime(long wakeUpTime); 15 | 16 | @QueryMethod 17 | long getWakeUpTime(); 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.updatabletimer; 2 | 3 | public class DynamicSleepWorkflowImpl implements DynamicSleepWorkflow { 4 | 5 | private UpdatableTimer timer = new UpdatableTimer(); 6 | 7 | @Override 8 | public void execute(long wakeUpTime) { 9 | timer.sleepUntil(wakeUpTime); 10 | } 11 | 12 | @Override 13 | public void updateWakeUpTime(long wakeUpTime) { 14 | timer.updateWakeUpTime(wakeUpTime); 15 | } 16 | 17 | @Override 18 | public long getWakeUpTime() { 19 | return timer.getWakeUpTime(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflowWorker.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.updatabletimer; 2 | 3 | import io.temporal.client.WorkflowClient; 4 | import io.temporal.serviceclient.WorkflowServiceStubs; 5 | import io.temporal.worker.Worker; 6 | import io.temporal.worker.WorkerFactory; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class DynamicSleepWorkflowWorker { 11 | 12 | static final String TASK_QUEUE = "TimerUpdate"; 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(DynamicSleepWorkflowWorker.class); 15 | 16 | /** Create just one workflow instance for the sake of the sample. */ 17 | static final String DYNAMIC_SLEEP_WORKFLOW_ID = "DynamicSleepWorkflow"; 18 | 19 | public static void main(String[] args) { 20 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 21 | WorkflowClient client = WorkflowClient.newInstance(service); 22 | WorkerFactory factory = WorkerFactory.newInstance(client); 23 | final Worker worker = factory.newWorker(TASK_QUEUE); 24 | worker.registerWorkflowImplementationTypes(DynamicSleepWorkflowImpl.class); 25 | factory.start(); 26 | logger.info("Worker started for task queue: " + TASK_QUEUE); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/updatabletimer/UpdatableTimer.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.updatabletimer; 2 | 3 | import io.temporal.workflow.Workflow; 4 | import java.time.Duration; 5 | import java.time.Instant; 6 | import org.slf4j.Logger; 7 | 8 | public final class UpdatableTimer { 9 | 10 | private final Logger logger = Workflow.getLogger(UpdatableTimer.class); 11 | 12 | private long wakeUpTime; 13 | private boolean wakeUpTimeUpdated; 14 | 15 | public void sleepUntil(long wakeUpTime) { 16 | logger.info("sleepUntil: " + Instant.ofEpochMilli(wakeUpTime)); 17 | this.wakeUpTime = wakeUpTime; 18 | while (true) { 19 | wakeUpTimeUpdated = false; 20 | Duration sleepInterval = Duration.ofMillis(this.wakeUpTime - Workflow.currentTimeMillis()); 21 | logger.info("Going to sleep for " + sleepInterval); 22 | if (!Workflow.await(sleepInterval, () -> wakeUpTimeUpdated)) { 23 | break; 24 | } 25 | } 26 | logger.info("sleepUntil completed"); 27 | } 28 | 29 | public void updateWakeUpTime(long wakeUpTime) { 30 | logger.info("updateWakeUpTime: " + Instant.ofEpochMilli(wakeUpTime)); 31 | this.wakeUpTime = wakeUpTime; 32 | this.wakeUpTimeUpdated = true; 33 | } 34 | 35 | public long getWakeUpTime() { 36 | return wakeUpTime; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/io/temporal/samples/updatabletimer/WakeUpTimeUpdater.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.updatabletimer; 2 | 3 | import static io.temporal.samples.updatabletimer.DynamicSleepWorkflowWorker.DYNAMIC_SLEEP_WORKFLOW_ID; 4 | 5 | import io.temporal.client.WorkflowClient; 6 | import io.temporal.serviceclient.WorkflowServiceStubs; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | public class WakeUpTimeUpdater { 11 | 12 | private static final Logger logger = LoggerFactory.getLogger(WakeUpTimeUpdater.class); 13 | 14 | public static void main(String[] args) { 15 | WorkflowServiceStubs service = WorkflowServiceStubs.newLocalServiceStubs(); 16 | WorkflowClient client = WorkflowClient.newInstance(service); 17 | 18 | // Create a stub that points to an existing workflow with the given ID 19 | DynamicSleepWorkflow workflow = 20 | client.newWorkflowStub(DynamicSleepWorkflow.class, DYNAMIC_SLEEP_WORKFLOW_ID); 21 | 22 | // signal workflow about the wake up time change 23 | workflow.updateWakeUpTime(System.currentTimeMillis() + 10000); 24 | logger.info("Updated wake up time to 10 seconds from now"); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/resources/dsl/sampleflow.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "sampleFlow", 3 | "name": "Sample Flow One", 4 | "description": "Sample Flow Definition", 5 | "actions": [ 6 | { 7 | "action": "One", 8 | "retries": 10, 9 | "startToCloseSec": 3 10 | }, 11 | { 12 | "action": "Two", 13 | "retries": 8, 14 | "startToCloseSec": 3 15 | }, 16 | { 17 | "action": "Three", 18 | "retries": 10, 19 | "startToCloseSec": 4 20 | }, 21 | { 22 | "action": "Four", 23 | "retries": 9, 24 | "startToCloseSec": 5 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/asyncchild/AsyncChildTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.asyncchild; 2 | 3 | import static org.junit.Assert.assertNotNull; 4 | 5 | import io.temporal.api.common.v1.WorkflowExecution; 6 | import io.temporal.client.WorkflowOptions; 7 | import io.temporal.testing.TestWorkflowRule; 8 | import org.junit.Rule; 9 | import org.junit.Test; 10 | 11 | public class AsyncChildTest { 12 | 13 | @Rule 14 | public TestWorkflowRule testWorkflowRule = 15 | TestWorkflowRule.newBuilder() 16 | .setWorkflowTypes(ParentWorkflowImpl.class, ChildWorkflowImpl.class) 17 | .build(); 18 | 19 | @Test 20 | public void testAsyncChildWorkflow() { 21 | ParentWorkflow parentWorkflow = 22 | testWorkflowRule 23 | .getWorkflowClient() 24 | .newWorkflowStub( 25 | ParentWorkflow.class, 26 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 27 | 28 | WorkflowExecution childExecution = parentWorkflow.executeParent(); 29 | 30 | assertNotNull(childExecution); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/getresultsasync/GetResultsAsyncTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.getresultsasync; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | import static org.junit.Assert.assertNotNull; 5 | 6 | import io.temporal.client.WorkflowOptions; 7 | import io.temporal.client.WorkflowStub; 8 | import io.temporal.testing.TestWorkflowRule; 9 | import java.util.concurrent.CompletableFuture; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | 13 | public class GetResultsAsyncTest { 14 | 15 | @Rule 16 | public TestWorkflowRule testWorkflowRule = 17 | TestWorkflowRule.newBuilder().setWorkflowTypes(MyWorkflowImpl.class).build(); 18 | 19 | @Test 20 | public void testGetResultsAsync() throws Exception { 21 | 22 | WorkflowStub workflowStub = 23 | testWorkflowRule 24 | .getWorkflowClient() 25 | .newUntypedWorkflowStub( 26 | "MyWorkflow", 27 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 28 | 29 | workflowStub.start(5); 30 | 31 | CompletableFuture completableFuture = workflowStub.getResultAsync(String.class); 32 | 33 | String result = completableFuture.get(); 34 | assertNotNull(result); 35 | assertEquals("woke up after 5 seconds", result); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/hello/HelloActivityExclusiveChoiceJUnit5Test.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.hello; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | 5 | import io.temporal.testing.TestWorkflowExtension; 6 | import org.junit.jupiter.api.Test; 7 | import org.junit.jupiter.api.extension.RegisterExtension; 8 | 9 | /** Unit test for {@link HelloActivityExclusiveChoice}. Doesn't use an external Temporal service. */ 10 | public class HelloActivityExclusiveChoiceJUnit5Test { 11 | 12 | @RegisterExtension 13 | public static final TestWorkflowExtension testWorkflowExtension = 14 | TestWorkflowExtension.newBuilder() 15 | .setWorkflowTypes(HelloActivityExclusiveChoice.PurchaseFruitsWorkflowImpl.class) 16 | .setActivityImplementations(new HelloActivityExclusiveChoice.OrderFruitsActivitiesImpl()) 17 | .build(); 18 | 19 | @Test 20 | public void testWorkflow(HelloActivityExclusiveChoice.PurchaseFruitsWorkflow workflow) { 21 | // Execute a workflow waiting for it to complete. 22 | HelloActivityExclusiveChoice.ShoppingList shoppingList = 23 | new HelloActivityExclusiveChoice.ShoppingList(); 24 | shoppingList.addFruitOrder(HelloActivityExclusiveChoice.Fruits.APPLE, 10); 25 | StringBuilder orderResults = workflow.orderFruit(shoppingList); 26 | assertEquals("Ordered 10 Apples...", orderResults.toString()); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/hello/HelloSideEffectTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.hello; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.testing.TestWorkflowRule; 7 | import org.junit.Rule; 8 | import org.junit.Test; 9 | 10 | public class HelloSideEffectTest { 11 | 12 | @Rule 13 | public TestWorkflowRule testWorkflowRule = 14 | TestWorkflowRule.newBuilder() 15 | .setWorkflowTypes(HelloSideEffect.SideEffectWorkflowImpl.class) 16 | .setActivityImplementations(new HelloSideEffect.SideEffectActivitiesImpl()) 17 | .build(); 18 | 19 | @Test 20 | public void testSideffectsWorkflow() { 21 | // Get a workflow stub using the same task queue the worker uses. 22 | HelloSideEffect.SideEffectWorkflow workflow = 23 | testWorkflowRule 24 | .getWorkflowClient() 25 | .newWorkflowStub( 26 | HelloSideEffect.SideEffectWorkflow.class, 27 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 28 | // Execute a workflow waiting for it to complete. 29 | String result = workflow.execute(); 30 | // make sure the result is same as the query result after workflow completion 31 | assertEquals(result, workflow.getResult()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/hello/HelloWorkflowTimerTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.hello; 2 | 3 | import io.temporal.client.WorkflowOptions; 4 | import io.temporal.testing.TestWorkflowRule; 5 | import org.junit.Assert; 6 | import org.junit.Rule; 7 | import org.junit.Test; 8 | 9 | public class HelloWorkflowTimerTest { 10 | @Rule 11 | public TestWorkflowRule testWorkflowRule = 12 | TestWorkflowRule.newBuilder() 13 | .setWorkflowTypes( 14 | HelloWorkflowTimer.WorkflowWithTimerImpl.class, 15 | HelloWorkflowTimer.WorkflowWithTimerChildWorkflowImpl.class) 16 | .setActivityImplementations(new HelloWorkflowTimer.WorkflowWithTimerActivitiesImpl()) 17 | .build(); 18 | 19 | @Test 20 | public void testWorkflowTimer() { 21 | HelloWorkflowTimer.WorkflowWithTimer workflow = 22 | testWorkflowRule 23 | .getWorkflowClient() 24 | .newWorkflowStub( 25 | HelloWorkflowTimer.WorkflowWithTimer.class, 26 | WorkflowOptions.newBuilder() 27 | .setWorkflowId("WorkflowWithTimerTestId") 28 | .setTaskQueue(testWorkflowRule.getTaskQueue()) 29 | .build()); 30 | 31 | String result = workflow.execute("test input"); 32 | Assert.assertEquals("Workflow timer fired while activities were executing.", result); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/polling/FrequentPollingTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.samples.polling.frequent.FrequentPollingActivityImpl; 7 | import io.temporal.samples.polling.frequent.FrequentPollingWorkflowImpl; 8 | import io.temporal.testing.TestWorkflowRule; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | 12 | public class FrequentPollingTest { 13 | @Rule 14 | public TestWorkflowRule testWorkflowRule = 15 | TestWorkflowRule.newBuilder() 16 | .setWorkflowTypes(FrequentPollingWorkflowImpl.class) 17 | .setActivityImplementations(new FrequentPollingActivityImpl(new TestService())) 18 | .build(); 19 | 20 | @Test 21 | public void testInfrequentPoll() { 22 | PollingWorkflow workflow = 23 | testWorkflowRule 24 | .getWorkflowClient() 25 | .newWorkflowStub( 26 | PollingWorkflow.class, 27 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 28 | 29 | assertEquals("OK", workflow.exec()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/polling/InfrequentPollingTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling; 2 | 3 | import static org.junit.Assert.*; 4 | 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.samples.polling.infrequent.InfrequentPollingActivityImpl; 7 | import io.temporal.samples.polling.infrequent.InfrequentPollingWorkflowImpl; 8 | import io.temporal.testing.TestWorkflowRule; 9 | import org.junit.Rule; 10 | import org.junit.Test; 11 | 12 | public class InfrequentPollingTest { 13 | @Rule 14 | public TestWorkflowRule testWorkflowRule = 15 | TestWorkflowRule.newBuilder() 16 | .setWorkflowTypes(InfrequentPollingWorkflowImpl.class) 17 | .setActivityImplementations(new InfrequentPollingActivityImpl(new TestService())) 18 | .build(); 19 | 20 | @Test 21 | public void testInfrequentPoll() { 22 | PollingWorkflow workflow = 23 | testWorkflowRule 24 | .getWorkflowClient() 25 | .newWorkflowStub( 26 | PollingWorkflow.class, 27 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 28 | 29 | assertEquals("OK", workflow.exec()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/test/java/io/temporal/samples/polling/PeriodicPollingTest.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.polling; 2 | 3 | import static org.junit.Assert.assertEquals; 4 | 5 | import io.temporal.client.WorkflowOptions; 6 | import io.temporal.samples.polling.periodicsequence.PeriodicPollingActivityImpl; 7 | import io.temporal.samples.polling.periodicsequence.PeriodicPollingChildWorkflowImpl; 8 | import io.temporal.samples.polling.periodicsequence.PeriodicPollingWorkflowImpl; 9 | import io.temporal.testing.TestWorkflowRule; 10 | import org.junit.Rule; 11 | import org.junit.Test; 12 | 13 | public class PeriodicPollingTest { 14 | @Rule 15 | public TestWorkflowRule testWorkflowRule = 16 | TestWorkflowRule.newBuilder() 17 | .setWorkflowTypes( 18 | PeriodicPollingWorkflowImpl.class, PeriodicPollingChildWorkflowImpl.class) 19 | .setActivityImplementations(new PeriodicPollingActivityImpl(new TestService())) 20 | .build(); 21 | 22 | @Test 23 | public void testInfrequentPoll() { 24 | PollingWorkflow workflow = 25 | testWorkflowRule 26 | .getWorkflowClient() 27 | .newWorkflowStub( 28 | PollingWorkflow.class, 29 | WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build()); 30 | 31 | assertEquals("OK", workflow.exec()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/test/resources/dsl/sampleflow.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "sampleFlow", 3 | "name": "Sample Flow One", 4 | "description": "Sample Flow Definition", 5 | "actions": [ 6 | { 7 | "action": "One", 8 | "retries": 10, 9 | "startToCloseSec": 3 10 | }, 11 | { 12 | "action": "Two", 13 | "retries": 8, 14 | "startToCloseSec": 3 15 | }, 16 | { 17 | "action": "Three", 18 | "retries": 10, 19 | "startToCloseSec": 4 20 | }, 21 | { 22 | "action": "Four", 23 | "retries": 9, 24 | "startToCloseSec": 5 25 | } 26 | ] 27 | } -------------------------------------------------------------------------------- /docker/github/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM eclipse-temurin:11-focal 2 | 3 | # Git is needed in order to update the dls submodule 4 | RUN apt-get update && apt-get install -y wget protobuf-compiler git 5 | 6 | RUN mkdir /temporal-java-samples 7 | WORKDIR /temporal-java-samples 8 | -------------------------------------------------------------------------------- /docker/github/README.md: -------------------------------------------------------------------------------- 1 | # Using Github Actions 2 | 3 | Github action simply runs Docker containers. So it is easy to perform the 4 | same build locally that Github will do. To handle this, there are 5 | two different docker-compose files: one for Github and one for local. 6 | The Dockerfile is the same for both. 7 | 8 | ## Testing the build locally 9 | To run the build locally, start from the root folder of this repo and run the following command: 10 | ```bash 11 | docker-compose -f docker/github/docker-compose.yaml run unit-test 12 | ``` 13 | 14 | Note that Github action will run basically the same commands. 15 | 16 | ## Testing the build in Github Actions 17 | Creating a PR against the main branch will trigger the Github action. 18 | -------------------------------------------------------------------------------- /docker/github/docker-compose.yaml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | unit-test: 5 | build: 6 | context: ../../ 7 | dockerfile: ./docker/github/Dockerfile 8 | command: "./gradlew --no-daemon test" 9 | environment: 10 | - "USER=unittest" 11 | volumes: 12 | - "../../:/temporal-java-samples" 13 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | springBootPluginVersion=2.7.13 2 | 3 | # use this for spring boot 3 support 4 | #springBootPluginVersion=3.2.0 5 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/temporalio/samples-java/c4217c9ce2a3a2e3567d714f4a28cc2ee8dc2048/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip 4 | networkTimeout=10000 5 | validateDistributionUrl=true 6 | zipStoreBase=GRADLE_USER_HOME 7 | zipStorePath=wrapper/dists 8 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'temporal-java-samples' 2 | include 'core' 3 | include 'springboot' 4 | include 'springboot-basic' 5 | -------------------------------------------------------------------------------- /springboot-basic/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation "org.springframework.boot:spring-boot-starter-web" 5 | implementation "org.springframework.boot:spring-boot-starter-thymeleaf" 6 | implementation "org.springframework.boot:spring-boot-starter-actuator" 7 | implementation "io.temporal:temporal-spring-boot-starter:$javaSDKVersion" 8 | testImplementation "org.springframework.boot:spring-boot-starter-test" 9 | runtimeOnly "io.micrometer:micrometer-registry-prometheus" 10 | dependencies { 11 | errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') 12 | if (JavaVersion.current().isJava11Compatible()) { 13 | errorprone('com.google.errorprone:error_prone_core:2.28.0') 14 | } else { 15 | errorprone('com.google.errorprone:error_prone_core:2.28.0') 16 | } 17 | } 18 | } 19 | 20 | bootJar { 21 | enabled = false 22 | } 23 | 24 | jar { 25 | enabled = true 26 | } -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/TemporalSpringbootSamplesApplication.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class TemporalSpringbootSamplesApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(TemporalSpringbootSamplesApplication.class, args).start(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import io.temporal.samples.springboot.hello.model.Person; 5 | 6 | @ActivityInterface 7 | public interface HelloActivity { 8 | String hello(Person person); 9 | } 10 | -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.samples.springboot.hello.model.Person; 4 | import io.temporal.spring.boot.ActivityImpl; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @ActivityImpl(taskQueues = "HelloSampleTaskQueue") 10 | public class HelloActivityImpl implements HelloActivity { 11 | @Value("${samples.data.language}") 12 | private String language; 13 | 14 | @Override 15 | public String hello(Person person) { 16 | String greeting = language.equals("spanish") ? "Hola " : "Hello "; 17 | return greeting + person.getFirstName() + " " + person.getLastName() + "!"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.samples.springboot.hello.model.Person; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface HelloWorkflow { 9 | @WorkflowMethod 10 | String sayHello(Person person); 11 | } 12 | -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.samples.springboot.hello.model.Person; 5 | import io.temporal.spring.boot.WorkflowImpl; 6 | import io.temporal.workflow.Workflow; 7 | import java.time.Duration; 8 | 9 | @WorkflowImpl(taskQueues = "HelloSampleTaskQueue") 10 | public class HelloWorkflowImpl implements HelloWorkflow { 11 | 12 | private HelloActivity activity = 13 | Workflow.newActivityStub( 14 | HelloActivity.class, 15 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 16 | 17 | @Override 18 | public String sayHello(Person person) { 19 | return activity.hello(person); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Hello Sample 2 | 3 | 1. Start SpringBoot from main samples repo directory: 4 | 5 | ./gradlew :springboot-basic:bootRun 6 | 7 | 2. In your browser navigate to: 8 | 9 | http://localhost:3030/hello 10 | 11 | Enter in first and last name in the form then click on Run Workflow 12 | to start workflow execution. Page will be updated to show the workflow 13 | execution results (the greeting). 14 | 15 | You can try changing the language setting in [application.yaml](../../../../../../resources/application.yaml) file 16 | from "english" to "spanish" to get the greeting result in Spanish. -------------------------------------------------------------------------------- /springboot-basic/src/main/java/io/temporal/samples/springboot/hello/model/Person.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello.model; 2 | 3 | public class Person { 4 | private String firstName; 5 | private String lastName; 6 | 7 | public Person() {} 8 | 9 | public Person(String firstName, String lastName) { 10 | this.firstName = firstName; 11 | this.lastName = lastName; 12 | } 13 | 14 | public String getFirstName() { 15 | return firstName; 16 | } 17 | 18 | public void setFirstName(String firstName) { 19 | this.firstName = firstName; 20 | } 21 | 22 | public String getLastName() { 23 | return lastName; 24 | } 25 | 26 | public void setLastName(String lastName) { 27 | this.lastName = lastName; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /springboot-basic/src/main/resources/application-tc.yaml: -------------------------------------------------------------------------------- 1 | spring.temporal: 2 | namespace: # https://docs.temporal.io/cloud/#temporal-cloud-namespace-id 3 | connection: 4 | target: .tmprl.cloud:7233 5 | mtls: 6 | key-file: /path/to/key.key 7 | cert-chain-file: /path/to/cert.pem 8 | 9 | # more configuration options https://github.com/temporalio/sdk-java/tree/master/temporal-spring-boot-autoconfigure#mtls -------------------------------------------------------------------------------- /springboot-basic/src/main/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 3030 3 | spring: 4 | main: 5 | allow-bean-definition-overriding: true 6 | application: 7 | name: temporal-samples 8 | # temporal specific configs 9 | temporal: 10 | namespace: default 11 | connection: 12 | target: 127.0.0.1:7233 13 | workersAutoDiscovery: 14 | packages: io.temporal.samples.springboot 15 | # specific for samples 16 | samples: 17 | data: 18 | language: english 19 | -------------------------------------------------------------------------------- /springboot-basic/src/main/resources/static/js/samplessse.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | 3 | var sse = $.SSE('/kafka-messages', { 4 | onMessage: function(e){ 5 | console.log(e); 6 | $('#kafka-messages tr:last').after(''+e.data+''); 7 | }, 8 | onError: function(e){ 9 | sse.stop(); 10 | console.log("Could not connect..Stopping SSE"); 11 | }, 12 | onEnd: function(e){ 13 | console.log("End"); 14 | } 15 | }); 16 | sse.start(); 17 | 18 | }); -------------------------------------------------------------------------------- /springboot-basic/src/main/resources/templates/fragments.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |

20 | 21 | -------------------------------------------------------------------------------- /springboot-basic/src/main/resources/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |
8 |
Temporal Java SDK Samples
9 |

Click on sample link to run it.

10 |
11 | Say Hello 12 |
13 |
14 |
15 |
16 | 17 | -------------------------------------------------------------------------------- /springboot-basic/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 3030 3 | spring: 4 | main: 5 | allow-bean-definition-overriding: true 6 | application: 7 | name: temporal-samples 8 | # temporal specific configs 9 | temporal: 10 | connection: 11 | target: 127.0.0.1:7233 12 | target.namespace: default 13 | workersAutoDiscovery: 14 | packages: io.temporal.samples.springboot 15 | # enable test server for testing 16 | test-server: 17 | enabled: true 18 | # specific for samples 19 | samples: 20 | data: 21 | language: english 22 | -------------------------------------------------------------------------------- /springboot/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'org.springframework.boot' 2 | 3 | dependencies { 4 | implementation "org.springframework.boot:spring-boot-starter-web" 5 | implementation "org.springframework.boot:spring-boot-starter-thymeleaf" 6 | implementation "org.springframework.boot:spring-boot-starter-actuator" 7 | implementation "org.springframework.boot:spring-boot-starter-data-jpa" 8 | implementation "org.springframework.kafka:spring-kafka" 9 | // we set this as impl depends to use embedded kafka in samples not just tests 10 | implementation "org.springframework.kafka:spring-kafka-test" 11 | implementation "io.temporal:temporal-spring-boot-starter:$javaSDKVersion" 12 | implementation "org.apache.camel.springboot:camel-spring-boot-starter:$camelVersion" 13 | implementation "org.apache.camel.springboot:camel-servlet-starter:$camelVersion" 14 | runtimeOnly "io.micrometer:micrometer-registry-prometheus" 15 | runtimeOnly "com.h2database:h2" 16 | testImplementation "org.springframework.boot:spring-boot-starter-test" 17 | dependencies { 18 | errorproneJavac('com.google.errorprone:javac:9+181-r4173-1') 19 | errorprone('com.google.errorprone:error_prone_core:2.28.0') 20 | } 21 | } 22 | 23 | bootJar { 24 | enabled = false 25 | } 26 | 27 | jar { 28 | enabled = true 29 | } -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/TemporalSpringbootSamplesApplication.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class TemporalSpringbootSamplesApplication { 8 | public static void main(String[] args) { 9 | SpringApplication.run(TemporalSpringbootSamplesApplication.class, args); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/actuator/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Actuator Worker Info Endpoint - Sample 2 | 3 | 1. Start SpringBoot from main samples repo directory: 4 | 5 | ./gradlew bootRun 6 | 7 | 2. In your browser navigate to: 8 | 9 | http://localhost:3030/actuator/temporalworkerinfo 10 | 11 | This sample shows how to create a custom Actuator Endpoint that 12 | displays registered workflow and activity implementations per task queue. 13 | This information comes from actually registered workers done by autoconfig module. -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/CamelConfig.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import org.apache.camel.CamelContext; 4 | import org.apache.camel.ConsumerTemplate; 5 | import org.apache.camel.ProducerTemplate; 6 | import org.apache.camel.component.servlet.CamelHttpTransportServlet; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.boot.web.servlet.ServletRegistrationBean; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.context.annotation.Profile; 12 | 13 | @Configuration() 14 | @Profile("!test") 15 | public class CamelConfig { 16 | @Autowired private CamelContext camelContext; 17 | 18 | @Bean 19 | ServletRegistrationBean servletRegistrationBean() { 20 | String contextPath = "/temporalapp"; 21 | ServletRegistrationBean servlet = 22 | new ServletRegistrationBean(new CamelHttpTransportServlet(), contextPath + "/*"); 23 | servlet.setName("CamelServlet"); 24 | return servlet; 25 | } 26 | 27 | @Bean 28 | ProducerTemplate producerTemplate() { 29 | return camelContext.createProducerTemplate(); 30 | } 31 | 32 | @Bean 33 | ConsumerTemplate consumerTemplate() { 34 | return camelContext.createConsumerTemplate(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/CamelResource.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import java.util.List; 4 | import org.apache.camel.ProducerTemplate; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.web.bind.annotation.GetMapping; 7 | import org.springframework.web.bind.annotation.ResponseBody; 8 | import org.springframework.web.bind.annotation.RestController; 9 | 10 | @RestController 11 | public class CamelResource { 12 | @Autowired private ProducerTemplate producerTemplate; 13 | 14 | @GetMapping("/orders") 15 | @ResponseBody 16 | public List getProductsByCategory() { 17 | producerTemplate.start(); 18 | List orders = producerTemplate.requestBody("direct:getOrders", null, List.class); 19 | producerTemplate.stop(); 20 | return orders; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/OrderActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import java.util.List; 5 | 6 | @ActivityInterface 7 | public interface OrderActivity { 8 | List getOrders(); 9 | } 10 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/OrderActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import io.temporal.spring.boot.ActivityImpl; 4 | import java.util.List; 5 | import org.apache.camel.ProducerTemplate; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Component; 8 | 9 | @Component 10 | @ActivityImpl(taskQueues = "CamelSampleTaskQueue") 11 | public class OrderActivityImpl implements OrderActivity { 12 | 13 | @Autowired private ProducerTemplate producerTemplate; 14 | 15 | @Override 16 | public List getOrders() { 17 | producerTemplate.start(); 18 | List orders = 19 | producerTemplate.requestBody("direct:findAllOrders", null, List.class); 20 | producerTemplate.stop(); 21 | return orders; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/OrderRepository.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface OrderRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/OrderWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | import java.util.List; 6 | 7 | @WorkflowInterface 8 | public interface OrderWorkflow { 9 | @WorkflowMethod 10 | public List start(); 11 | } 12 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/OrderWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.camel; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.spring.boot.WorkflowImpl; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | import java.util.List; 8 | 9 | @WorkflowImpl(taskQueues = "CamelSampleTaskQueue") 10 | public class OrderWorkflowImpl implements OrderWorkflow { 11 | 12 | private OrderActivity activity = 13 | Workflow.newActivityStub( 14 | OrderActivity.class, 15 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 16 | 17 | @Override 18 | public List start() { 19 | return activity.getOrders(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/camel/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Camel Sample 2 | 3 | 1. Start SpringBoot from main samples repo directory: 4 | 5 | ./gradlew :springboot:bootRun 6 | 7 | 2. In your browser navigate to: 8 | 9 | http://localhost:3030/orders 10 | 11 | This sample starts an Apache Camel route which starts our orders Workflow. 12 | The workflow starts an activity which starts Camel route to get all orders JPA. -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.customize; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface CustomizeActivity { 7 | String run(String input); 8 | } 9 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.customize; 2 | 3 | import io.temporal.spring.boot.ActivityImpl; 4 | import org.springframework.stereotype.Component; 5 | 6 | @Component 7 | @ActivityImpl(taskQueues = "CustomizeTaskQueue") 8 | public class CustomizeActivityImpl implements CustomizeActivity { 9 | @Override 10 | public String run(String input) { 11 | return "Completed as " + input + " activity!"; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.customize; 2 | 3 | import io.temporal.workflow.WorkflowInterface; 4 | import io.temporal.workflow.WorkflowMethod; 5 | 6 | @WorkflowInterface 7 | public interface CustomizeWorkflow { 8 | @WorkflowMethod 9 | String execute(); 10 | } 11 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/customize/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Customize Options Sample 2 | 3 | This sample shows how to optimize default options such as 4 | * WorkflowServiceStubsOptions 5 | * WorkflowClientOption 6 | * WorkerFactoryOptions 7 | * WorkerOptions 8 | 9 | WorkerOptions can be optimized per worker/task queue. 10 | 11 | For this sample we set our specific worker to be "local activity worker" via custom options meaning 12 | it would not poll for activity tasks. Click on "Run Workflow" button to start instance of 13 | our sample workflow. This workflow will try to invoke our activity as "normal" 14 | activity which should time out on ScheduleToClose timeout, then we invoke this activity 15 | as local activity which should succeed. 16 | 17 | ## How to run 18 | 1. Start SpringBoot from main samples repo directory: 19 | 20 | ./gradlew :springboot:bootRun 21 | 22 | 2. In your browser navigate to: 23 | 24 | http://localhost:3030/customize 25 | 26 | 3. Press the "Run Workflow" button to start execution. You will see result show on page in 4 seconds -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/HelloActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import io.temporal.samples.springboot.hello.model.Person; 5 | 6 | @ActivityInterface 7 | public interface HelloActivity { 8 | String hello(Person person); 9 | } 10 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/HelloActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.samples.springboot.hello.model.Person; 4 | import io.temporal.spring.boot.ActivityImpl; 5 | import org.springframework.beans.factory.annotation.Value; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | @ActivityImpl(taskQueues = "HelloSampleTaskQueue") 10 | public class HelloActivityImpl implements HelloActivity { 11 | @Value("${samples.data.language}") 12 | private String language; 13 | 14 | @Override 15 | public String hello(Person person) { 16 | String greeting = language.equals("spanish") ? "Hola " : "Hello "; 17 | return greeting + person.getFirstName() + " " + person.getLastName() + "!"; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.samples.springboot.hello.model.Person; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface HelloWorkflow { 9 | @WorkflowMethod 10 | String sayHello(Person person); 11 | } 12 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.samples.springboot.hello.model.Person; 5 | import io.temporal.spring.boot.WorkflowImpl; 6 | import io.temporal.workflow.Workflow; 7 | import java.time.Duration; 8 | 9 | @WorkflowImpl(taskQueues = "HelloSampleTaskQueue") 10 | public class HelloWorkflowImpl implements HelloWorkflow { 11 | 12 | private HelloActivity activity = 13 | Workflow.newActivityStub( 14 | HelloActivity.class, 15 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 16 | 17 | @Override 18 | public String sayHello(Person person) { 19 | return activity.hello(person); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Hello Sample 2 | 3 | 1. Start SpringBoot from main samples repo directory: 4 | 5 | ./gradlew :springboot:bootRun 6 | 7 | 2. In your browser navigate to: 8 | 9 | http://localhost:3030/hello 10 | 11 | Enter in first and last name in the form then click on Run Workflow 12 | to start workflow execution. Page will be updated to show the workflow 13 | execution results (the greeting). 14 | 15 | You can try changing the language setting in [application.yaml](../../../../../../resources/application.yaml) file 16 | from "english" to "spanish" to get the greeting result in Spanish. -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/hello/model/Person.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.hello.model; 2 | 3 | public class Person { 4 | private String firstName; 5 | private String lastName; 6 | 7 | public Person() {} 8 | 9 | public Person(String firstName, String lastName) { 10 | this.firstName = firstName; 11 | this.lastName = lastName; 12 | } 13 | 14 | public String getFirstName() { 15 | return firstName; 16 | } 17 | 18 | public void setFirstName(String firstName) { 19 | this.firstName = firstName; 20 | } 21 | 22 | public String getLastName() { 23 | return lastName; 24 | } 25 | 26 | public void setLastName(String lastName) { 27 | this.lastName = lastName; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/kafka/KafkaActivity.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.kafka; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | 5 | @ActivityInterface 6 | public interface KafkaActivity { 7 | void sendMessage(String message); 8 | } 9 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/kafka/KafkaActivityImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.kafka; 2 | 3 | import io.temporal.failure.ApplicationFailure; 4 | import io.temporal.spring.boot.ActivityImpl; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.beans.factory.annotation.Value; 7 | import org.springframework.kafka.core.KafkaTemplate; 8 | import org.springframework.stereotype.Component; 9 | 10 | @Component 11 | @ActivityImpl(taskQueues = "KafkaSampleTaskQueue") 12 | public class KafkaActivityImpl implements KafkaActivity { 13 | 14 | // Setting required to false means we won't fail 15 | // if a test does not have kafka enabled 16 | @Autowired(required = false) 17 | private KafkaTemplate kafkaTemplate; 18 | 19 | @Value(value = "${samples.message.topic.name}") 20 | private String topicName; 21 | 22 | @Override 23 | public void sendMessage(String message) { 24 | try { 25 | kafkaTemplate.send(topicName, message).get(); 26 | } catch (Exception e) { 27 | throw ApplicationFailure.newFailure( 28 | "Unable to send message.", e.getClass().getName(), e.getMessage()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageController.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.kafka; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; 8 | 9 | @RestController 10 | public class MessageController { 11 | private final List emitters = new ArrayList<>(); 12 | 13 | @GetMapping("/kafka-messages") 14 | public SseEmitter getKafkaMessages() { 15 | 16 | SseEmitter emitter = new SseEmitter(60 * 1000L); 17 | emitters.add(emitter); 18 | 19 | emitter.onCompletion(() -> emitters.remove(emitter)); 20 | 21 | emitter.onTimeout(() -> emitters.remove(emitter)); 22 | 23 | return emitter; 24 | } 25 | 26 | public List getEmitters() { 27 | return emitters; 28 | } 29 | 30 | public SseEmitter getLatestEmitter() { 31 | return emitters.isEmpty() ? null : emitters.get(emitters.size() - 1); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.kafka; 2 | 3 | import io.temporal.workflow.SignalMethod; 4 | import io.temporal.workflow.WorkflowInterface; 5 | import io.temporal.workflow.WorkflowMethod; 6 | 7 | @WorkflowInterface 8 | public interface MessageWorkflow { 9 | 10 | @WorkflowMethod 11 | void start(); 12 | 13 | @SignalMethod 14 | void update(String message); 15 | } 16 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageWorkflowImpl.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.kafka; 2 | 3 | import io.temporal.activity.ActivityOptions; 4 | import io.temporal.spring.boot.WorkflowImpl; 5 | import io.temporal.workflow.Workflow; 6 | import java.time.Duration; 7 | 8 | @WorkflowImpl(taskQueues = "KafkaSampleTaskQueue") 9 | public class MessageWorkflowImpl implements MessageWorkflow { 10 | 11 | private KafkaActivity activity = 12 | Workflow.newActivityStub( 13 | KafkaActivity.class, 14 | ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()); 15 | private String message = null; 16 | 17 | @Override 18 | public void start() { 19 | Workflow.await(() -> message != null); 20 | // simulate some steps / milestones 21 | activity.sendMessage( 22 | "Starting execution: " + Workflow.getInfo().getWorkflowId() + " with message: " + message); 23 | 24 | activity.sendMessage("Step 1 done..."); 25 | activity.sendMessage("Step 2 done..."); 26 | activity.sendMessage("Step 3 done..."); 27 | 28 | activity.sendMessage("Completing execution: " + Workflow.getInfo().getWorkflowId()); 29 | } 30 | 31 | @Override 32 | public void update(String message) { 33 | this.message = message; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/metrics/README.md: -------------------------------------------------------------------------------- 1 | # SpringBoot Metrics Sample 2 | 3 | 1. Start SpringBoot from main samples repo directory: 4 | 5 | ./gradlew :springboot:bootRun 6 | 7 | 2. In your browser navigate to: 8 | 9 | http://localhost:3030/metrics 10 | 11 | This sample involves just SpringBoot and Actuator configurations which are 12 | included in the samples already. The page shows information on how to set up 13 | SDK metrics in your SpringBoot applications. -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/ProductNotAvailableForAmountException.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update; 2 | 3 | public class ProductNotAvailableForAmountException extends Exception { 4 | public ProductNotAvailableForAmountException(String message) { 5 | super(message); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseActivities.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update; 2 | 3 | import io.temporal.activity.ActivityInterface; 4 | import io.temporal.samples.springboot.update.model.Purchase; 5 | 6 | @ActivityInterface 7 | public interface PurchaseActivities { 8 | boolean isProductInStockForPurchase(Purchase purchase); 9 | 10 | boolean makePurchase(Purchase purchase); 11 | } 12 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseWorkflow.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update; 2 | 3 | import io.temporal.samples.springboot.update.model.Purchase; 4 | import io.temporal.workflow.*; 5 | 6 | @WorkflowInterface 7 | public interface PurchaseWorkflow { 8 | @WorkflowMethod 9 | void start(); 10 | 11 | @UpdateMethod 12 | boolean makePurchase(Purchase purchase); 13 | 14 | @UpdateValidatorMethod(updateName = "makePurchase") 15 | void makePurchaseValidator(Purchase purchase); 16 | 17 | @SignalMethod 18 | void exit(); 19 | } 20 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/ResourceNotFoundException.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update; 2 | 3 | import org.springframework.http.HttpStatus; 4 | import org.springframework.web.bind.annotation.ResponseStatus; 5 | 6 | @ResponseStatus(value = HttpStatus.NOT_FOUND) 7 | public class ResourceNotFoundException extends Exception { 8 | public ResourceNotFoundException(String message) { 9 | super(message); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/model/ProductRepository.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update.model; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | import org.springframework.stereotype.Repository; 5 | 6 | @Repository 7 | public interface ProductRepository extends JpaRepository {} 8 | -------------------------------------------------------------------------------- /springboot/src/main/java/io/temporal/samples/springboot/update/model/Purchase.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot.update.model; 2 | 3 | public class Purchase { 4 | int product; 5 | int amount; 6 | 7 | public Purchase() {} 8 | 9 | public Purchase(int product, int amount) { 10 | this.product = product; 11 | this.amount = amount; 12 | } 13 | 14 | public int getProduct() { 15 | return product; 16 | } 17 | 18 | public void setProduct(int product) { 19 | this.product = product; 20 | } 21 | 22 | public int getAmount() { 23 | return amount; 24 | } 25 | 26 | public void setAmount(int amount) { 27 | this.amount = amount; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /springboot/src/main/resources/application-tc.yaml: -------------------------------------------------------------------------------- 1 | spring.temporal: 2 | namespace: # https://docs.temporal.io/cloud/#temporal-cloud-namespace-id 3 | connection: 4 | target: .tmprl.cloud:7233 5 | mtls: 6 | key-file: /path/to/key.key 7 | cert-chain-file: /path/to/cert.pem 8 | 9 | # more configuration options https://github.com/temporalio/sdk-java/tree/master/temporal-spring-boot-autoconfigure#mtls -------------------------------------------------------------------------------- /springboot/src/main/resources/static/js/samplessse.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | 3 | var sse = $.SSE('/kafka-messages', { 4 | onMessage: function(e){ 5 | console.log(e); 6 | $('#kafka-messages tr:last').after(''+e.data+''); 7 | }, 8 | onError: function(e){ 9 | sse.stop(); 10 | console.log("Could not connect..Stopping SSE"); 11 | }, 12 | onEnd: function(e){ 13 | console.log("End"); 14 | } 15 | }); 16 | sse.start(); 17 | 18 | }); -------------------------------------------------------------------------------- /springboot/src/main/resources/templates/actuator.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 |
7 |
8 |

Temporal Java SDK Samples

9 |
In this sample we show how to create a custom Actuator Endpoint showing Worker Info.
10 |

11 |
12 |
Spring Actuator allows 13 | us to create custom endpoints. This sample shows how to create a Worker Info custom endpoint that shows registered Workflow and Activity impls per task queue.

14 | View the custom endpoint at localhost:3030/actuator/temporalworkerinfo 15 |
16 |
17 |
18 |
19 | 20 | -------------------------------------------------------------------------------- /springboot/src/main/resources/templates/fragments.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /springboot/src/test/java/io/temporal/samples/springboot/KafkaConsumerTestHelper.java: -------------------------------------------------------------------------------- 1 | package io.temporal.samples.springboot; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | import org.apache.kafka.clients.consumer.ConsumerRecord; 5 | import org.springframework.kafka.annotation.KafkaListener; 6 | import org.springframework.stereotype.Component; 7 | 8 | @Component 9 | public class KafkaConsumerTestHelper { 10 | private CountDownLatch latch = new CountDownLatch(5); 11 | private String payload = null; 12 | 13 | @KafkaListener(id = "samples-test-id", topics = "${samples.message.topic.name}") 14 | public void receive(ConsumerRecord consumerRecord) { 15 | 16 | setPayload(consumerRecord.toString()); 17 | latch.countDown(); 18 | } 19 | 20 | public CountDownLatch getLatch() { 21 | return latch; 22 | } 23 | 24 | public String getPayload() { 25 | return payload; 26 | } 27 | 28 | private void setPayload(String payload) { 29 | this.payload = payload; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /springboot/src/test/resources/application.yaml: -------------------------------------------------------------------------------- 1 | server: 2 | port: 3030 3 | spring: 4 | main: 5 | allow-bean-definition-overriding: true 6 | application: 7 | name: temporal-samples 8 | # temporal specific configs 9 | temporal: 10 | connection: 11 | target: 127.0.0.1:7233 12 | target.namespace: default 13 | workersAutoDiscovery: 14 | packages: io.temporal.samples.springboot 15 | # enable test server for testing 16 | test-server: 17 | enabled: true 18 | # data source config for tests that need it 19 | datasource: 20 | url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE; 21 | username: sa 22 | password: pass 23 | driver-class-name: org.h2.Driver 24 | jpa: 25 | database-platform: org.hibernate.dialect.H2Dialect 26 | hibernate: 27 | ddl-auto: create-drop 28 | defer-datasource-initialization: true 29 | ## kafka setup for samples 30 | kafka: 31 | consumer: 32 | auto-offset-reset: earliest 33 | bootstrap-servers: ${spring.embedded.kafka.brokers} 34 | # specific for samples 35 | samples: 36 | data: 37 | language: english 38 | message: 39 | topic: 40 | name: samples-test-topic 41 | group: 42 | name: samples-group 43 | -------------------------------------------------------------------------------- /springboot/src/test/resources/data.sql: -------------------------------------------------------------------------------- 1 | INSERT INTO product(name, code, description, price, stock) VALUES ('Zoom U-Tale Worm', 'w1', 'The U-Tale Worms are another one of Zooms truly-impressive big bass baits.', 5, 20); 2 | INSERT INTO product(name, code, description, price, stock) VALUES ('Yamamoto Baits 5" Senko', 'w2', 'Yamamoto Baits 5" Senko has become a mainstay for bass throughout the United States.', 7, 15); 3 | INSERT INTO product(name, code, description, price, stock) VALUES ('Rapala Original Floating Minnow', 'w3', 'The Rapala Original Floating Minnow is the lure that started it all and is still one of the most popular lures around.', 10, 10); 4 | INSERT INTO product(name, code, description, price, stock) VALUES ('Z-Man Jackhammer', 'w4', 'Exclusive patented ChatterBait bladed swim jig design and stainless hex-shaped ChatterBlade.', 18, 10); 5 | INSERT INTO product(name, code, description, price, stock) VALUES ('Roboworm Straight Tail Worm Bait', 'w5', 'Roboworms are the most consistant poured baits on the market.', 9, 30); --------------------------------------------------------------------------------