[
  {
    "path": ".github/CODEOWNERS",
    "content": "# Primary owners\n\n* @tsurdilo @temporalio/sdk @antmendoza\n\n# Below are owners for samples for modules\n# that are owned by teams other than the SDK team.\n# For each one, we add the owning team, as well as\n# @temporalio/sdk, so the SDK team can continue to\n# manage repo-wide concerns\n/springai/ @temporalio/ai-sdk @temporalio/sdk"
  },
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"gradle\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n  - package-ecosystem: \"github-actions\"\n    directory: \"/\"\n    schedule:\n      interval: \"weekly\"\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: \"Continuous Integration\"\non: [push, pull_request]\npermissions:\n  contents: read\n\njobs:\n  validation:\n    name: \"Gradle wrapper validation\"\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n      - uses: gradle/actions/wrapper-validation@ac396bf1a80af16236baf54bd7330ae21dc6ece5 # v6\n\n  unittest:\n    name: Unit Tests\n    runs-on: ubuntu-latest\n    timeout-minutes: 15\n    steps:\n    - name: Checkout repo\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n      with:\n        fetch-depth: 0\n        submodules: recursive\n        ref: ${{ github.event.pull_request.head.sha }}\n\n    - name: Run unit tests\n      run: |\n        docker compose -f ./docker/github/docker-compose.yaml up --exit-code-from unit-test unit-test\n\n  code_format:\n    name:  Code format\n    runs-on: ubuntu-latest\n    timeout-minutes: 20\n    steps:\n    - name: Checkout repo\n      uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6\n      with:\n        fetch-depth: 0\n        submodules: recursive\n        ref: ${{ github.event.pull_request.head.sha }}\n\n    - name: Set up Java\n      uses: actions/setup-java@be666c2fcd27ec809703dec50e508c2fdc7f6654 # v5\n      with:\n        java-version: \"17\"\n        distribution: \"temurin\"\n\n    - name: Set up Gradle\n      uses: gradle/actions/setup-gradle@ac396bf1a80af16236baf54bd7330ae21dc6ece5 # v6\n\n    - name: Run copyright and code format checks\n      run: ./gradlew --no-daemon spotlessCheck\n"
  },
  {
    "path": ".gitignore",
    "content": "target\n.*.swp\n.*.swo\n*.iml\n.DS_Store\n.idea\n.gradle\n**/build/\n**/out/\n.classpath\n.project\n.settings/\nbin/\ncore/.vscode/\n.claude/\n"
  },
  {
    "path": "LICENSE",
    "content": "Temporal Java SDK\n\nCopyright (c) 2025 Temporal Technologies, Inc. All Rights Reserved\n\nCopyright (c) 2017 Uber Technologies, Inc. All Rights Reserved\n\nCopyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved\n\nLicensed under the Apache License, Version 2.0 (the \"License\"); you may not use this\nfile except in compliance with the License. You may obtain a copy of the License at\n\nhttp://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software distributed under\nthe License is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR\nCONDITIONS OF ANY KIND, either express or implied. See the License for the\nspecific language governing permissions and limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "# Temporal Java SDK Samples\n\nThis repository contains samples that demonstrate various capabilities of \nTemporal using the [Java SDK](https://github.com/temporalio/sdk-java).\n\nIt contains the following modules:\n* [Core](/core): showcases many different SDK features.\n* [SpringBoot](/springboot): showcases SpringBoot autoconfig integration.\n* [SpringBoot Basic](/springboot-basic): Minimal sample showing SpringBoot autoconfig integration without any extra external dependencies.\n* [Spring AI](/springai): demonstrates the Temporal Spring AI integration — durable AI agents with chat models, tools, MCP servers, vector stores, and embeddings.\n\n## Learn more about Temporal and Java SDK\n\n- [Temporal Server repo](https://github.com/temporalio/temporal)\n- [Java SDK repo](https://github.com/temporalio/sdk-java)\n- [Java SDK Guide](https://docs.temporal.io/dev-guide/java)\n\n## Requirements\n\n- Java 17+\n- Local Temporal Server, easiest to get started would be using [Temporal CLI](https://github.com/temporalio/cli).\nFor more options see docs [here](https://docs.temporal.io/kb/all-the-ways-to-run-a-cluster).\n\n\n## Build and run tests\n\n1. Clone this repository:\n\n       git clone https://github.com/temporalio/samples-java\n       cd samples-java\n\n2. Build and run Tests\n\n       ./gradlew build\n\n## Running Samples:\n\nYou can run both \"Core\" and \"SpringBoot\" samples from the main samples project directory.\nDetails on how to run each sample can be found in following two sections.\nTo skip to SpringBoot samples click [here](#Running-SpringBoot-Samples).\n\n### Running \"Core\" samples\nSee the README.md file in each main sample directory for cut/paste Gradle command to run specific example.\n\n<!-- @@@SNIPSTART samples-java-readme-samples-directory -->\n\n#### Hello samples\n\n- [**Hello**](/core/src/main/java/io/temporal/samples/hello): This sample includes a number of individual Workflows that can be executed independently. Each one demonstrates something specific.\n\n    - [**HelloAccumulator**](/core/src/main/java/io/temporal/samples/hello/HelloAccumulator.java): Demonstrates a Workflow Definition that accumulates signals and continues as new.\n    - [**HelloActivity**](/core/src/main/java/io/temporal/samples/hello/HelloActivity.java): Demonstrates a Workflow Definition that executes a single Activity.\n    - [**HelloActivityRetry**](/core/src/main/java/io/temporal/samples/hello/HelloActivityRetry.java): Demonstrates how to Retry an Activity Execution.\n    - [**HelloActivityExclusiveChoice**](/core/src/main/java/io/temporal/samples/hello/HelloActivityExclusiveChoice.java): Demonstrates how to execute Activities based on dynamic input.\n    - [**HelloAsync**](/core/src/main/java/io/temporal/samples/hello/HelloAsync.java): Demonstrates how to execute Activities asynchronously and wait for them using Promises.\n    - [**HelloAwait**](/core/src/main/java/io/temporal/samples/hello/HelloAwait.java): Demonstrates how to use Await statement to wait for a condition.\n    - [**HelloParallelActivity**](/core/src/main/java/io/temporal/samples/hello/HelloParallelActivity.java): Demonstrates how to execute multiple Activities in parallel, asynchronously, and wait for them using `Promise.allOf`.\n    - [**HelloAsyncActivityCompletion**](/core/src/main/java/io/temporal/samples/hello/HelloAsyncActivityCompletion.java): Demonstrates how to complete an Activity Execution asynchronously.\n    - [**HelloAsyncLambda**](/core/src/main/java/io/temporal/samples/hello/HelloAsyncLambda.java): Demonstrates how to execute part of a Workflow asynchronously in a separate task (thread).\n    - [**HelloCancellationScope**](/core/src/main/java/io/temporal/samples/hello/HelloCancellationScope.java): Demonstrates how to explicitly cancel parts of a Workflow Execution.\n    - [**HelloCancellationScopeWithTimer**](/core/src/main/java/io/temporal/samples/hello/HelloCancellationScopeWithTimer.java): Demonstrates how to cancel activity when workflow timer fires and complete execution. This can prefered over using workflow run/execution timeouts.\n    - [**HelloDetachedCancellationScope**](/core/src/main/java/io/temporal/samples/hello/HelloDetachedCancellationScope.java): Demonstrates how to execute cleanup code after a Workflow Execution has been explicitly cancelled.\n    - [**HelloChild**](/core/src/main/java/io/temporal/samples/hello/HelloChild.java): Demonstrates how to execute a simple Child Workflow.\n    - [**HelloCron**](/core/src/main/java/io/temporal/samples/hello/HelloCron.java): Demonstrates how to execute a Workflow according to a cron schedule.\n    - [**HelloDynamic**](/core/src/main/java/io/temporal/samples/hello/HelloDynamic.java): Demonstrates how to use `DynamicWorkflow` and `DynamicActivity` interfaces.\n    - [**HelloEagerWorkflowStart**](/core/src/main/java/io/temporal/samples/hello/HelloEagerWorkflowStart.java): Demonstrates the use of a eager workflow start.\n    - [**HelloPeriodic**](/core/src/main/java/io/temporal/samples/hello/HelloPeriodic.java): Demonstrates the use of the Continue-As-New feature.\n    - [**HelloException**](/core/src/main/java/io/temporal/samples/hello/HelloException.java): Demonstrates how to handle exception propagation and wrapping.\n    - [**HelloLocalActivity**](/core/src/main/java/io/temporal/samples/hello/HelloLocalActivity.java): Demonstrates the use of a [Local Activity](https://docs.temporal.io/docs/jargon/mesh/#local-activity).\n    - [**HelloPolymorphicActivity**](/core/src/main/java/io/temporal/samples/hello/HelloPolymorphicActivity.java): Demonstrates Activity Definitions that extend a common interface.\n    - [**HelloQuery**](/core/src/main/java/io/temporal/samples/hello/HelloQuery.java): Demonstrates how to Query the state of a Workflow Execution.\n    - [**HelloSchedules**](/core/src/main/java/io/temporal/samples/hello/HelloSchedules.java): Demonstrates how to create and interact with a Schedule.\n    - [**HelloSignal**](/core/src/main/java/io/temporal/samples/hello/HelloSignal.java): Demonstrates how to send and handle a Signal.\n    - [**HelloSaga**](/core/src/main/java/io/temporal/samples/hello/HelloSaga.java): Demonstrates how to use the SAGA feature.\n    - [**HelloSearchAttributes**](/core/src/main/java/io/temporal/samples/hello/HelloSearchAttributes.java): Demonstrates how to add custom Search Attributes to Workflow Executions.\n    - [**HelloSideEffect**](/core/src/main/java/io/temporal/samples/hello/HelloSideEffect.java)**: Demonstrates how to implement a Side Effect.\n    - [**HelloUpdate**](/core/src/main/java/io/temporal/samples/hello/HelloUpdate.java): Demonstrates how to create and interact with an Update.\n    - [**HelloDelayedStart**](/core/src/main/java/io/temporal/samples/hello/HelloDelayedStart.java): Demonstrates how to use delayed start config option when starting a Workflow Executions.\n    - [**HelloSignalWithTimer**](/core/src/main/java/io/temporal/samples/hello/HelloSignalWithTimer.java): Demonstrates how to use collect signals for certain amount of time and then process last one. \n    - [**HelloWorkflowTimer**](/core/src/main/java/io/temporal/samples/hello/HelloWorkflowTimer.java): Demonstrates how we can use workflow timer to restrict duration of workflow execution instead of workflow run/execution timeouts.\n    - [**Auto-Heartbeating**](/core/src/main/java/io/temporal/samples/autoheartbeat/): Demonstrates use of Auto-heartbeating utility via activity interceptor. \n    - [**HelloSignalWithStartAndWorkflowInit**](/core/src/main/java/io/temporal/samples/hello/HelloSignalWithStartAndWorkflowInit.java): Demonstrates how WorkflowInit can be useful with SignalWithStart to initialize workflow variables.\n    - [**HelloStandaloneActivity**](/core/src/main/java/io/temporal/samples/hello/HelloStandaloneActivity.java): Demonstrates how to execute a Standalone Activity directly from an ActivityClient, without a Workflow.\n\n#### Scenario-based samples\n\n- [**File Processing Sample**](/core/src/main/java/io/temporal/samples/fileprocessing): 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.\n\n- [**Booking SAGA**](/core/src/main/java/io/temporal/samples/bookingsaga): Demonstrates Temporals take on the Camunda BPMN \"trip booking\" example.\n\n- [**Synchronous Booking SAGA**](/core/src/main/java/io/temporal/samples/bookingsyncsaga): Demonstrates low latency SAGA with potentially long compensations.\n\n- [**Money Transfer**](/core/src/main/java/io/temporal/samples/moneytransfer): Demonstrates the use of a dedicated Activity Worker.\n\n- [**Money Batch**](/core/src/main/java/io/temporal/samples/moneybatch): Demonstrates a situation where a single deposit should be initiated for multiple withdrawals. For example, a seller might want to be paid once per fixed number of transactions. This sample can be easily extended to perform a payment based on more complex criteria, such as at a specific time or an accumulated amount. The sample also demonstrates how to Signal the Workflow when it executes (*Signal with start*). If the Workflow is already executing, it just receives the Signal. If it is not executing, then the Workflow executes first, and then the Signal is delivered to it. *Signal with start* is a \"lazy\" way to execute Workflows when Signaling them.\n\n- [**Domain-Specific-Language - Define sequence of steps in JSON**](/core/src/main/java/io/temporal/samples/dsl): Demonstrates using domain specific language (DSL) defined in JSON to specify sequence of steps to be performed in our workflow.\n\n- [**Polling Services**](/core/src/main/java/io/temporal/samples/polling): Recommended implementation of an activity that needs to periodically poll an external resource waiting its successful completion\n\n- [**Heartbeating Activity Batch**](/core/src/main/java/io/temporal/samples/batch/heartbeatingactivity): Batch job implementation using a heartbeating activity.\n\n- [**Iterator Batch**](/core/src/main/java/io/temporal/samples/batch/iterator): Batch job implementation using the workflow iterator pattern.\n\n- [**Sliding Window Batch**](/core/src/main/java/io/temporal/samples/batch/slidingwindow): A batch implementation that maintains a configured number of child workflows during processing.\n\n- [**Safe Message Passing**](/core/src/main/java/io/temporal/samples/safemessagepassing): Safely handling concurrent updates and signals messages.\n\n- [**Custom Annotation**](/core/src/main/java/io/temporal/samples/customannotation): Demonstrates how to create a custom annotation using an interceptor.\n\n- [**Async Packet Delivery**](/core/src/main/java/io/temporal/samples/packetdelivery): Demonstrates running multiple execution paths async within single execution.\n\n- [**Worker Versioning**](/core/src/main/java/io/temporal/samples/workerversioning): Demonstrates how to use worker versioning to manage workflow code changes.\n\n- [**Environment Configuration**](/core/src/main/java/io/temporal/samples/envconfig):\nLoad client configuration from TOML files with programmatic overrides.\n\n#### API demonstrations\n\n- [**Async Untyped Child Workflow**](/core/src/main/java/io/temporal/samples/asyncuntypedchild): Demonstrates how to invoke an untyped child workflow async, that can complete after parent workflow is already completed.\n\n- [**Updatable Timer**](/core/src/main/java/io/temporal/samples/updatabletimer): Demonstrates the use of a helper class which relies on `Workflow.await` to implement a blocking sleep that can be updated at any moment.\n\n- [**Workflow Count Interceptor**](/core/src/main/java/io/temporal/samples/countinterceptor): Demonstrates how to create and register a simple Workflow Count Interceptor.\n\n- [**Workflow Retry On Signal Interceptor**](/core/src/main/java/io/temporal/samples/retryonsignalinterceptor): Demonstrates how to create and register an interceptor that retries an activity on a signal.\n\n- [**List Workflows**](/core/src/main/java/io/temporal/samples/listworkflows): Demonstrates the use of custom search attributes and ListWorkflowExecutionsRequest with custom queries.\n\n- [**Payload Converter - CloudEvents**](/core/src/main/java/io/temporal/samples/payloadconverter/cloudevents): Demonstrates the use of a custom payload converter for CloudEvents.\n\n- [**Payload Converter - Crypto**](/core/src/main/java/io/temporal/samples/payloadconverter/crypto): Demonstrates the use of a custom payload converter using jackson-json-crypto.\n\n- [**Async Child Workflow**](/core/src/main/java/io/temporal/samples/asyncchild): Demonstrates how to invoke a child workflow async, that can complete after parent workflow is already completed.\n\n- [**Terminate Workflow**](/core/src/main/java/io/temporal/samples/terminateworkflow): Demonstrates how to terminate a workflow using client API.\n\n- [**Get Workflow Results Async**](/core/src/main/java/io/temporal/samples/getresultsasync): Demonstrates how to start and get workflow results in async manner.\n\n- [**Per Activity Type Options**](/core/src/main/java/io/temporal/samples/peractivityoptions): Demonstrates how to set per Activity type options.\n\n- [**Configure WorkflowClient to use mTLS**](/core/src/main/java/io/temporal/samples/ssl): Demonstrates how to configure WorkflowClient when using mTLS.\n\n- [**Configure WorkflowClient to use API Key**](/core/src/main/java/io/temporal/samples/apikey): Demonstrates how to configure WorkflowClient when using API Keys.\n\n- [**Payload Codec**](/core/src/main/java/io/temporal/samples/encodefailures): Demonstrates how to use simple codec to encode/decode failure messages.\n\n- [**Exclude Workflow/ActivityTypes from Interceptors**](/core/src/main/java/io/temporal/samples/excludefrominterceptor): Demonstrates how to exclude certain workflow / activity types from interceptors.\n\n- [**Standalone Activities**](/core/src/main/java/io/temporal/samples/standaloneactivities): Demonstrates how to start, execute, list, and count Standalone Activities — Activities that run independently without a Workflow, using ActivityClient.\n\n#### SDK Metrics\n\n- [**Set up SDK metrics**](/core/src/main/java/io/temporal/samples/metrics): Demonstrates how to set up and scrape SDK metrics.\n\n#### Tracing Support\n\n- [**Set up OpenTracing and/or OpenTelemetry with Jaeger**](/core/src/main/java/io/temporal/samples/tracing): Demonstrates how to set up OpenTracing and/or OpenTelemetry and view traces using Jaeger.\n\n#### Encryption Support\n\n- [**Encrypted Payloads**](/core/src/main/java/io/temporal/samples/encryptedpayloads): Demonstrates how to use simple codec to encrypt and decrypt payloads.\n\n- [**AWS Encryption SDK**](/core/src/main/java/io/temporal/samples/keymanagementencryption/awsencryptionsdk): Demonstrates how to use the AWS Encryption SDK to encrypt and decrypt payloads with AWS KMS.\n\n#### Nexus Samples\n\n- [**Getting Started**](/core/src/main/java/io/temporal/samples/nexus): Demonstrates how to get started with Temporal and Nexus.\n\n- [**Mapping Multiple Arguments**](/core/src/main/java/io/temporal/samples/nexus): Demonstrates how map a Nexus operation to a Workflow that takes multiple arguments.\n\n- [**Cancellation**](/core/src/main/java/io/temporal/samples/nexuscancellation): Demonstrates how to cancel an async Nexus operation.\n\n- [**Context/Header Propagation**](/core/src/main/java/io/temporal/samples/nexuscontextpropagation): Demonstrates how to propagate context through Nexus operation headers.\n\n- [**Nexus Messaging**](/core/src/main/java/io/temporal/samples/nexusmessaging): Demonstrates how to send signal, update and query messages through Nexus.\n  This contains two samples, one sending messages to an existing workflow and a second that creates a workflow through Nexus\n  and sends messages to it.\n<!-- @@@SNIPEND -->\n\n### Running SpringBoot Samples\n\nThese samples use SpringBoot 2 by default. To switch to using SpringBoot 3 look at the [gradle.properties](gradle.properties) file\nand follow simple instructions there.\n\n1. Start SpringBoot from main repo dir:\n\n       ./gradlew :springboot:bootRun\n\nTo run the basic sample run\n\n       ./gradlew :springboot-basic:bootRun\n\n\n2. Navigate to [localhost:3030](http://localhost:3030)\n\n3. Select which sample you want to run\n\nMore info on each sample:\n- [**Hello**](/springboot/src/main/java/io/temporal/samples/springboot/hello): Invoke simple \"Hello\" workflow from a GET endpoint\n- [**SDK Metrics**](/springboot/src/main/java/io/temporal/samples/springboot/metrics): Learn how to set up SDK Metrics\n- [**Synchronous Update**](/springboot/src/main/java/io/temporal/samples/springboot/update): Learn how to use Synchronous Update feature with this purchase sample\n- [**Kafka Request / Reply**](/springboot/src/main/java/io/temporal/samples/springboot/kafka): Sample showing possible integration with event streaming platforms such as Kafka\n- [**Customize Options**](/springboot/src/main/java/io/temporal/samples/springboot/customize): Sample showing how to customize options such as WorkerOptions, WorkerFactoryOptions, etc (see options config [here](springboot/src/main/java/io/temporal/samples/springboot/customize/TemporalOptionsConfig.java))\n- [**Apache Camel Route**](/springboot/src/main/java/io/temporal/samples/springboot/camel): Sample showing how to start Workflow execution from a Camel Route\n- [**Custom Actuator Endpoint**](/springboot/src/main/java/io/temporal/samples/springboot/actuator): Sample showing how to create a custom Actuator endpoint that shows registered Workflow and Activity impls per task queue.\n\n#### Temporal Cloud\nTo run any of the SpringBoot samples in your Temporal Cloud namespace:\n\n1. Edit the [application-tc.yaml](/springboot/src/main/resources/application-tc.yaml) to set your namespace and client certificates.\n\n2. Start SpringBoot from main repo dir with the `tc` profile:\n\n       ./gradlew bootRun --args='--spring.profiles.active=tc'\n\n3. Follow the previous section from step 2\n\n### Running Spring AI Samples\n\nThe Spring AI samples demonstrate the [Temporal Spring AI integration](https://github.com/temporalio/sdk-java/tree/master/temporal-spring-ai), which makes Spring AI agents durable on Temporal — model calls run as Temporal Activities recorded in Workflow history, and tools are dispatched per their type so they fit Workflow execution.\n\nEach sample is its own Spring Boot application with an interactive CLI. Run from the main repo dir:\n\n       ./gradlew :springai:basic:bootRun\n       ./gradlew :springai:mcp:bootRun\n       ./gradlew :springai:multimodel:bootRun\n       ./gradlew :springai:rag:bootRun\n\nAll samples need an `OPENAI_API_KEY` environment variable; some need additional setup (see each sample's source for details).\n\nMore info on each sample:\n- [**Basic**](/springai/basic): Chat workflow with three tool flavors — activity-backed (`WeatherActivity`), plain workflow tools (`StringTools`), and `@SideEffectTool` (`TimestampTools`) — plus a `PromptChatMemoryAdvisor` for conversation history.\n- [**MCP**](/springai/mcp): Connects to a Model Context Protocol server and exposes its tools to the AI through Temporal activities. Defaults to the filesystem MCP server.\n- [**Multi-Model**](/springai/multimodel): Two providers in one workflow (OpenAI and Anthropic), per-model `ActivityOptions` overrides via a Spring bean, plus a route that exercises Anthropic's extended-thinking mode through provider-specific `ChatOptions` pass-through. Requires `ANTHROPIC_API_KEY` in addition to `OPENAI_API_KEY`.\n- [**RAG**](/springai/rag): Vector store + embeddings for retrieval-augmented generation. Add documents, then ask questions; the workflow searches the vector store and grounds the answer in the retrieved context.\n"
  },
  {
    "path": "build.gradle",
    "content": "plugins {\n    id \"net.ltgt.errorprone\" version \"4.0.1\"\n    id 'com.diffplug.spotless' version '6.25.0' apply false\n    id \"org.springframework.boot\" version \"${springBootPluginVersion}\"\n}\n\nsubprojects {\n    apply plugin: 'java'\n    apply plugin: 'net.ltgt.errorprone'\n    apply plugin: 'com.diffplug.spotless'\n\n    compileJava {\n        options.compilerArgs << \"-Werror\"\n    }\n\n    java {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n\n    ext {\n        otelVersion = '1.30.1'\n        otelVersionAlpha = \"${otelVersion}-alpha\"\n        javaSDKVersion = '1.35.0'\n        camelVersion = '3.22.1'\n        jarVersion = '1.0.0'\n    }\n\n    repositories {\n        mavenLocal()\n        maven {\n            url \"https://oss.sonatype.org/content/repositories/snapshots/\"\n        }\n        mavenCentral()\n    }\n\n    dependencies {\n\n    }\n\n    apply plugin: 'com.diffplug.spotless'\n\n    spotless {\n        java {\n            target 'src/*/java/**/*.java'\n            targetExclude '**/.idea/**'\n            googleJavaFormat('1.24.0')\n        }\n    }\n\n    compileJava.dependsOn 'spotlessApply'\n\n    test {\n        useJUnitPlatform()\n    }\n}"
  },
  {
    "path": "core/build.gradle",
    "content": "dependencies {\n    // Temporal SDK\n    implementation \"io.temporal:temporal-sdk:$javaSDKVersion\"\n    implementation \"io.temporal:temporal-opentracing:$javaSDKVersion\"\n    testImplementation(\"io.temporal:temporal-testing:$javaSDKVersion\")\n\n    // Environment configuration\n    implementation \"io.temporal:temporal-envconfig:$javaSDKVersion\"\n\n    // Needed for SDK related functionality\n    implementation \"io.grpc:grpc-util\"\n    implementation(platform(\"com.fasterxml.jackson:jackson-bom:2.17.2\"))\n    implementation \"com.fasterxml.jackson.core:jackson-databind\"\n    implementation \"com.fasterxml.jackson.core:jackson-core\"\n\n    implementation \"io.micrometer:micrometer-registry-prometheus\"\n\n    implementation group: 'ch.qos.logback', name: 'logback-classic', version: '1.5.6'\n    implementation group: 'com.jayway.jsonpath', name: 'json-path', version: '2.9.0'\n\n    implementation(platform(\"io.opentelemetry:opentelemetry-bom:$otelVersion\"))\n    implementation \"io.opentelemetry:opentelemetry-sdk\"\n    implementation \"io.opentelemetry:opentelemetry-exporter-jaeger\"\n    implementation \"io.opentelemetry:opentelemetry-extension-trace-propagators\"\n    implementation \"io.opentelemetry:opentelemetry-opentracing-shim:$otelVersionAlpha\"\n    implementation \"io.opentelemetry:opentelemetry-semconv:$otelVersionAlpha\"\n    implementation 'io.jaegertracing:jaeger-client:1.8.1'\n\n    // Used in samples\n    implementation group: 'commons-configuration', name: 'commons-configuration', version: '1.10'\n    implementation group: 'io.cloudevents', name: 'cloudevents-core', version: '4.0.1'\n    implementation group: 'io.cloudevents', name: 'cloudevents-api', version: '4.0.1'\n    implementation group: 'io.cloudevents', name: 'cloudevents-json-jackson', version: '3.0.0'\n    implementation group: 'net.thisptr', name: 'jackson-jq', version: '1.0.0-preview.20240207'\n    implementation group: 'commons-cli', name: 'commons-cli', version: '1.9.0'\n\n    // Used in AWS Encryption SDK sample\n    implementation group: 'com.amazonaws', name: 'aws-encryption-sdk-java', version: '3.0.1'\n    implementation(\"software.amazon.cryptography:aws-cryptographic-material-providers:1.0.2\")\n    implementation(platform(\"software.amazon.awssdk:bom:2.20.91\"))\n    implementation(\"software.amazon.awssdk:kms\")\n    implementation(\"software.amazon.awssdk:dynamodb\")\n\n    // we don't update it to 2.1.0 because 2.1.0 requires Java 11\n    implementation 'com.codingrodent:jackson-json-crypto:1.1.0'\n\n    testImplementation \"junit:junit:4.13.2\"\n    testImplementation \"org.mockito:mockito-core:5.12.0\"\n\n    testImplementation(platform(\"org.junit:junit-bom:5.10.3\"))\n    testImplementation \"org.junit.jupiter:junit-jupiter-api\"\n    testRuntimeOnly \"org.junit.jupiter:junit-jupiter-engine\"\n    testRuntimeOnly \"org.junit.vintage:junit-vintage-engine\"\n\n    dependencies {\n        errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')\n        errorprone('com.google.errorprone:error_prone_core:2.28.0')\n    }\n}\n\ntask execute(type: JavaExec) {\n    mainClass = findProperty(\"mainClass\") ?: \"\"\n    classpath = sourceSets.main.runtimeClasspath\n    if (findProperty(\"args\")) {\n        args findProperty(\"args\").tokenize()\n    }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/apikey/ApiKeyWorker.java",
    "content": "package io.temporal.samples.apikey;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class ApiKeyWorker {\n  static final String TASK_QUEUE = \"MyTaskQueue\";\n\n  public static void main(String[] args) throws Exception {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // For temporal cloud this would be ${cloud-region}.{cloud}.api.temporal.io:7233\n    // Example us-east-1.aws.api.temporal.io:7233\n    String targetEndpoint = System.getenv(\"TEMPORAL_ENDPOINT\");\n    // Your registered namespace.\n    String namespace = System.getenv(\"TEMPORAL_NAMESPACE\");\n    // Your API Key\n    String apiKey = System.getenv(\"TEMPORAL_API_KEY\");\n\n    if (targetEndpoint == null || namespace == null || apiKey == null) {\n      throw new IllegalArgumentException(\n          \"TEMPORAL_ENDPOINT, TEMPORAL_NAMESPACE, and TEMPORAL_API_KEY environment variables must be set\");\n    }\n\n    // Create API Key enabled client with environment config as base\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(\n            WorkflowServiceStubsOptions.newBuilder(profile.toWorkflowServiceStubsOptions())\n                .setTarget(targetEndpoint)\n                .setEnableHttps(true)\n                .addApiKey(() -> apiKey)\n                .build());\n\n    // Now setup and start workflow worker\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder(profile.toWorkflowClientOptions())\n                .setNamespace(namespace)\n                .build());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    System.out.println(\"Worker started. Press Ctrl+C to exit.\");\n    // Keep the worker running\n    Thread.currentThread().join();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/apikey/MyWorkflow.java",
    "content": "package io.temporal.samples.apikey;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/apikey/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.apikey;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n  @Override\n  public String execute() {\n    return \"done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/apikey/README.md",
    "content": "# Workflow execution with API Key\n\nThis example shows how to secure your Temporal application with API Key authentication.\nThis is required to connect with Temporal Cloud or any production Temporal deployment that uses API Key authentication.\n\n## Prerequisites\n\n1. A Temporal Cloud account\n2. A namespace in Temporal Cloud\n3. An API Key for your namespace\n\n## Getting your API Key\n\n1. Log in to your Temporal Cloud account\n2. Navigate to your namespace\n3. Go to Namespace Settings > API Keys\n4. Click \"Create API Key\"\n5. Give your API Key a name and select the appropriate permissions\n6. Copy the API Key value (you won't be able to see it again)\n\n## Export env variables\n\nBefore running the example you need to export the following env variables: \n\n```bash\n# Your Temporal Cloud endpoint (e.g., us-east-1.aws.api.temporal.io:7233)\nexport TEMPORAL_ENDPOINT=\"us-east-1.aws.api.temporal.io:7233\"\n\n# Your Temporal Cloud namespace\nexport TEMPORAL_NAMESPACE=\"your-namespace\"\n\n# Your API Key from Temporal Cloud\nexport TEMPORAL_API_KEY=\"your-api-key\"\n```\n\n## Running this sample\n\nThis sample consists of two components that need to be run in separate terminals:\n\n1. First, start the worker:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.apikey.ApiKeyWorker\n```\n\n2. Then, in a new terminal, run the starter:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.apikey.Starter\n```\n\n## Expected result\n\nWhen running the worker, you should see:\n```text\n[main] INFO  i.t.s.WorkflowServiceStubsImpl - Created WorkflowServiceStubs for channel: ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=us-east-1.aws.api.temporal.io:7233}} \n[main] INFO  io.temporal.internal.worker.Poller - start: Poller{name=Workflow Poller taskQueue=\"MyTaskQueue\", namespace=\"your-namespace\"} \nWorker started. Press Ctrl+C to exit.\n```\n\nWhen running the starter, you should see:\n```text\n[main] INFO  i.t.s.WorkflowServiceStubsImpl - Created WorkflowServiceStubs for channel: ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=us-east-1.aws.api.temporal.io:7233}} \n[main] INFO  io.temporal.internal.worker.Poller - start: Poller{name=Workflow Poller taskQueue=\"MyTaskQueue\", namespace=\"your-namespace\"} \ndone\n```\n\n## Troubleshooting\n\nIf you encounter any issues:\n\n1. Verify your environment variables are set correctly:\n   ```bash\n   echo $TEMPORAL_ENDPOINT\n   echo $TEMPORAL_NAMESPACE\n   echo $TEMPORAL_API_KEY\n   ```\n\n2. Check that your API Key has the correct permissions for your namespace\n\n3. Ensure your namespace is active and accessible\n\n4. If you get connection errors, verify your endpoint is correct and accessible from your network\n\n5. Make sure you're running the commands from the correct directory (where the `gradlew` script is located)\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/apikey/Starter.java",
    "content": "package io.temporal.samples.apikey;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class Starter {\n\n  static final String TASK_QUEUE = \"MyTaskQueue\";\n  static final String WORKFLOW_ID = \"HelloAPIKeyWorkflow\";\n\n  public static void main(String[] args) throws Exception {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // For temporal cloud this would be ${cloud-region}.{cloud}.api.temporal.io:7233\n    // Example us-east-1.aws.api.temporal.io:7233\n    String targetEndpoint = System.getenv(\"TEMPORAL_ENDPOINT\");\n    // Your registered namespace.\n    String namespace = System.getenv(\"TEMPORAL_NAMESPACE\");\n    // Your API Key\n    String apiKey = System.getenv(\"TEMPORAL_API_KEY\");\n\n    if (targetEndpoint == null || namespace == null || apiKey == null) {\n      throw new IllegalArgumentException(\n          \"TEMPORAL_ENDPOINT, TEMPORAL_NAMESPACE, and TEMPORAL_API_KEY environment variables must be set\");\n    }\n\n    // Create API Key enabled client with environment config as base\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(\n            WorkflowServiceStubsOptions.newBuilder(profile.toWorkflowServiceStubsOptions())\n                .setTarget(targetEndpoint)\n                .setEnableHttps(true)\n                .addApiKey(() -> apiKey)\n                .build());\n\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder(profile.toWorkflowClientOptions())\n                .setNamespace(namespace)\n                .build());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    MyWorkflow workflow =\n        client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    String greeting = workflow.execute();\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/ChildWorkflow.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface ChildWorkflow {\n  @WorkflowMethod\n  String executeChild();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/ChildWorkflowImpl.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class ChildWorkflowImpl implements ChildWorkflow {\n  @Override\n  public String executeChild() {\n    Workflow.sleep(Duration.ofSeconds(3));\n    return \"Child workflow done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/ParentWorkflow.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface ParentWorkflow {\n  @WorkflowMethod\n  WorkflowExecution executeParent();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/ParentWorkflowImpl.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.ParentClosePolicy;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\n\npublic class ParentWorkflowImpl implements ParentWorkflow {\n  @Override\n  public WorkflowExecution executeParent() {\n\n    // We set the parentClosePolicy to \"Abandon\"\n    // This will allow child workflow to continue execution after parent completes\n    ChildWorkflowOptions childWorkflowOptions =\n        ChildWorkflowOptions.newBuilder()\n            .setWorkflowId(\"childWorkflow\")\n            .setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON)\n            .build();\n\n    // Get the child workflow stub\n    ChildWorkflow child = Workflow.newChildWorkflowStub(ChildWorkflow.class, childWorkflowOptions);\n    // Start the child workflow async\n    Async.function(child::executeChild);\n    // Get the child workflow execution promise\n    Promise<WorkflowExecution> childExecution = Workflow.getWorkflowExecution(child);\n    // Call .get on the promise. This will block until the child workflow starts execution (or start\n    // fails)\n    return childExecution.get();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/README.md",
    "content": "# Async Child Workflow execution\n\nThe sample demonstrates shows how to invoke a Child Workflow asynchronously.\nThe Child Workflow is allowed to complete its execution even after the Parent Workflow completes. \n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.asyncchild.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncchild/Starter.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\n\npublic class Starter {\n\n  public static final String TASK_QUEUE = \"asyncChildTaskQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    createWorker(factory);\n\n    WorkflowOptions parentWorkflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(\"parentWorkflow\")\n            .setTaskQueue(TASK_QUEUE)\n            .build();\n    ParentWorkflow parentWorkflowStub =\n        client.newWorkflowStub(ParentWorkflow.class, parentWorkflowOptions);\n\n    // Start parent workflow and wait for it to complete\n    WorkflowExecution childWorkflowExecution = parentWorkflowStub.executeParent();\n\n    // Get the child workflow execution status (after parent completed)\n    System.out.println(\n        \"Child execution status: \" + getStatusAsString(childWorkflowExecution, client, service));\n\n    // Wait for child workflow to complete\n    sleep(4);\n\n    // Check the status of the child workflow again\n    System.out.println(\n        \"Child execution status: \" + getStatusAsString(childWorkflowExecution, client, service));\n\n    System.exit(0);\n  }\n\n  private static void createWorker(WorkerFactory factory) {\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(ParentWorkflowImpl.class, ChildWorkflowImpl.class);\n\n    factory.start();\n  }\n\n  private static String getStatusAsString(\n      WorkflowExecution execution, WorkflowClient client, WorkflowServiceStubs service) {\n    DescribeWorkflowExecutionRequest describeWorkflowExecutionRequest =\n        DescribeWorkflowExecutionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setExecution(execution)\n            .build();\n\n    DescribeWorkflowExecutionResponse resp =\n        service.blockingStub().describeWorkflowExecution(describeWorkflowExecutionRequest);\n\n    WorkflowExecutionInfo workflowExecutionInfo = resp.getWorkflowExecutionInfo();\n    return workflowExecutionInfo.getStatus().toString();\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (Exception e) {\n      System.out.println(\"Exception: \" + e.getMessage());\n      System.exit(0);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/ChildWorkflow.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * Define the child workflow Interface. It must contain one method annotated with @WorkflowMethod\n *\n * @see WorkflowInterface\n * @see WorkflowMethod\n */\n@WorkflowInterface\npublic interface ChildWorkflow {\n\n  /**\n   * Define the child workflow method. This method is executed when the workflow is started. The\n   * workflow completes when the workflow method finishes execution.\n   */\n  @WorkflowMethod\n  String composeGreeting(String greeting, String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/ChildWorkflowImpl.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport io.temporal.workflow.Workflow;\n\n/**\n * Define the parent workflow implementation. It implements the getGreeting workflow method\n *\n * <p>Note that a workflow implementation must always be public for the Temporal library to be able\n * to create its instances.\n */\npublic class ChildWorkflowImpl implements ChildWorkflow {\n\n  @Override\n  public String composeGreeting(String greeting, String name) {\n\n    // Sleep for 2 seconds to ensure the child completes after the parent.\n    Workflow.sleep(2000);\n\n    return greeting + \" \" + name + \"!\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/ParentWorkflow.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * Define the parent workflow interface. It must contain one method annotated with @WorkflowMethod\n *\n * @see WorkflowInterface\n * @see WorkflowMethod\n */\n@WorkflowInterface\npublic interface ParentWorkflow {\n\n  /**\n   * Define the parent workflow method. This method is executed when the workflow is started. The\n   * workflow completes when the workflow method finishes execution.\n   */\n  @WorkflowMethod\n  String getGreeting(String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/ParentWorkflowImpl.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport static io.temporal.samples.asyncuntypedchild.Starter.WORKFLOW_ID;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.ParentClosePolicy;\nimport io.temporal.workflow.*;\n\n// Define the parent workflow implementation. It implements the getGreeting workflow method\npublic class ParentWorkflowImpl implements ParentWorkflow {\n\n  @Override\n  public String getGreeting(String name) {\n    /*\n     * Define the child workflow stub. Since workflows are stateful,\n     * a new stub must be created for each child workflow.\n     */\n    ChildWorkflowStub child =\n        Workflow.newUntypedChildWorkflowStub(\n            ChildWorkflow.class.getSimpleName(),\n            ChildWorkflowOptions.newBuilder()\n                .setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON)\n                .setWorkflowId(\"Child_of_\" + WORKFLOW_ID)\n                .build());\n\n    /*\n     * Invoke the child workflows composeGreeting workflow method async.\n     * This call is non-blocking and returns immediately returning a {@link io.temporal.workflow.Promise},\n     * you can invoke `get()` on the returned promise to wait for the child workflow result.\n     */\n    child.executeAsync(String.class, \"Hello\", name);\n\n    // Wait for the child workflow to start before returning the result\n    Promise<WorkflowExecution> childExecution = child.getExecution();\n    WorkflowExecution childWorkflowExecution = childExecution.get();\n\n    // return the child workflowId\n    return childWorkflowExecution.getWorkflowId();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/README.md",
    "content": "# Async Child Workflow execution\n\nThe sample demonstrates shows how to invoke an Untyped Child Workflow asynchronously.\nThe Child Workflow continues running for some time after the Parent Workflow completes. \n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.asyncuntypedchild.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/asyncuntypedchild/Starter.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\n/**\n * Sample Temporal Workflow Definition that demonstrates the execution of a Child Workflow. Child\n * workflows allow you to group your Workflow logic into small logical and reusable units that solve\n * a particular problem. They can be typically reused by multiple other Workflows.\n */\npublic class Starter {\n\n  static final String WORKFLOW_ID = \"ParentWithAsyncUntypedChild\";\n\n  static final String TASK_QUEUE = WORKFLOW_ID + \"Queue\";\n\n  /**\n   * With the workflow, and child workflow defined, we can now start execution. The main method is\n   * the workflow starter.\n   */\n  public static void main(String[] args) {\n\n    // Get a Workflow service stub.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the parent and child workflow implementation with the worker.\n     * Since workflows are stateful in nature,\n     * we need to register the workflow types.\n     */\n    worker.registerWorkflowImplementationTypes(ParentWorkflowImpl.class, ChildWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Start a workflow execution. Usually this is done from another program.\n    // Uses task queue from the GreetingWorkflow @WorkflowMethod annotation.\n\n    // Create our parent workflow client stub. It is used to start the parent workflow execution.\n    ParentWorkflow workflow =\n        client.newWorkflowStub(\n            ParentWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Execute our parent workflow and wait for it to complete, it returns the child workflow id.\n    String childWorkflowId = workflow.getGreeting(\"World\");\n    System.out.println(\"Child WorkflowId=[\" + childWorkflowId + \"] started in abandon mode\");\n\n    String childResult = client.newUntypedWorkflowStub(childWorkflowId).getResult(String.class);\n\n    System.out.println(\"Result from child workflow = \" + childResult);\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/AutoHeartbeatUtil.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat;\n\nimport io.temporal.activity.ActivityExecutionContext;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.ScheduledExecutorService;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\npublic class AutoHeartbeatUtil {\n  private final long period;\n  private final long initialDelay;\n  private final TimeUnit periodTimeUnit;\n  private final ScheduledExecutorService timerService =\n      Executors.newSingleThreadScheduledExecutor();\n  private final ActivityExecutionContext context;\n  private final Object details;\n  private String heartbeaterId;\n\n  public AutoHeartbeatUtil(\n      long period,\n      long initialDelay,\n      TimeUnit periodTimeUnit,\n      ActivityExecutionContext context,\n      Object details) {\n    this.period = period;\n    this.initialDelay = initialDelay;\n    this.periodTimeUnit = periodTimeUnit;\n    this.context = context;\n    this.details = details;\n    // Set to activity id better, for sample we just use type\n    heartbeaterId = context.getInfo().getActivityType();\n  }\n\n  public ScheduledFuture<?> start() {\n    System.out.println(\"Autoheartbeater[\" + heartbeaterId + \"] starting...\");\n    return timerService.scheduleAtFixedRate(\n        () -> {\n          //          try {\n          System.out.println(\n              \"Autoheartbeater[\"\n                  + heartbeaterId\n                  + \"]\"\n                  + \"heartbeating at: \"\n                  + printShortCurrentTime());\n          context.heartbeat(details);\n        },\n        initialDelay,\n        period,\n        periodTimeUnit);\n  }\n\n  public void stop() {\n    System.out.println(\"Autoheartbeater[\" + heartbeaterId + \"] being requested to stop.\");\n    // Try not to execute another heartbeat that could have been queued up\n    // Note this can at times take a second or two so make sure to test this out on your workers\n    // So can set best heartbeat timeout (sometimes might need larger value to accomodate)\n    timerService.shutdownNow();\n  }\n\n  private String printShortCurrentTime() {\n    return DateTimeFormatter.ofPattern(\"HH:mm:ss\")\n        .withZone(ZoneId.systemDefault())\n        .format(Instant.now());\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/README.md",
    "content": "# Auto-heartbeating sample for activities that define HeartbeatTimeout\n\nThis sample shows an implementation of an \"auto-heartbeating\" utility that can be applied via interceptor to all\nactivities where you define HeartbeatTimeout. Use case where this can be helpful include situations where you have\nlong-running activities where you want to heartbeat but its difficult to explicitly call heartbeat api in activity code\ndirectly.\nAnother useful scenario for this is where you have activity that at times can complete in very short amount of time,\nbut then at times can take for example minutes. In this case you have to set longer StartToClose timeout \nbut you might not want first heartbeat to be sent right away but send it after the \"shorter\" duration of activity\nexecution. \n\nWarning: make sure to test this sample for your use case. This includes load testing. This sample was not \ntested on large scale workloads. In addition note that it is recommended to heartbeat from activity code itself. Using\nthis type of autoheartbeating utility does have disatvantage that activity code itself can continue running after \na handled activity cancelation. Please be aware of these warnings when applying this sample.\n\n1. Start the Sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.autoheartbeat.Starter\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/Starter.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.samples.autoheartbeat.activities.AutoActivitiesImpl;\nimport io.temporal.samples.autoheartbeat.interceptor.AutoHeartbeatWorkerInterceptor;\nimport io.temporal.samples.autoheartbeat.workflows.AutoWorkflow;\nimport io.temporal.samples.autoheartbeat.workflows.AutoWorkflowImpl;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.io.IOException;\n\npublic class Starter {\n  static final String TASK_QUEUE = \"AutoheartbeatTaskQueue\";\n  static final String WORKFLOW_ID = \"AutoHeartbeatWorkflow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Configure our auto heartbeat workflow interceptor which will apply\n    // AutoHeartbeaterUtil to each activity workflow schedules which has a heartbeat\n    // timeout configured\n    WorkerFactoryOptions wfo =\n        WorkerFactoryOptions.newBuilder()\n            .setWorkerInterceptors(new AutoHeartbeatWorkerInterceptor())\n            .build();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client, wfo);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(AutoWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new AutoActivitiesImpl());\n\n    factory.start();\n\n    // first run completes execution with autoheartbeat utils\n    firstRun(client);\n    // second run cancels running (pending) activity via signal (specific scope cancel)\n    secondRun(client);\n    // third run cancels running execution which cancels activity as well\n    thirdRun(client);\n    // fourth run turns off autoheartbeat for activities and lets activity time out on heartbeat\n    // timeout\n    fourthRun(client);\n\n    System.exit(0);\n  }\n\n  @SuppressWarnings(\"unused\")\n  private static void firstRun(WorkflowClient client) {\n    System.out.println(\"**** First Run: run workflow to completion\");\n    AutoWorkflow firstRun =\n        client.newWorkflowStub(\n            AutoWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    try {\n      String firstRunResult = firstRun.exec(\"Auto heartbeating is cool\");\n      System.out.println(\"First run result: \" + firstRunResult);\n    } catch (Exception e) {\n      System.out.println(\"First run - Workflow exec exception: \" + e.getClass().getName());\n    }\n  }\n\n  @SuppressWarnings(\"unused\")\n  private static void secondRun(WorkflowClient client) {\n    System.out.println(\"\\n\\n**** Second Run: cancel activities via signal\");\n    AutoWorkflow secondRun =\n        client.newWorkflowStub(\n            AutoWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    WorkflowClient.start(secondRun::exec, \"Auto heartbeating is cool\");\n    doSleeps(4);\n    secondRun.cancelActivity();\n\n    try {\n      String secondRunResult = WorkflowStub.fromTyped(secondRun).getResult(String.class);\n      System.out.println(\"Second run result: \" + secondRunResult);\n    } catch (Exception e) {\n      System.out.println(\"Second run - Workflow exec exception: \" + e.getClass().getName());\n    }\n  }\n\n  @SuppressWarnings(\"unused\")\n  private static void thirdRun(WorkflowClient client) {\n    System.out.println(\"\\n\\n**** Third Run: cancel workflow execution\");\n    AutoWorkflow thirdRun =\n        client.newWorkflowStub(\n            AutoWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    WorkflowClient.start(thirdRun::exec, \"Auto heartbeating is cool\");\n    doSleeps(10);\n    try {\n      WorkflowStub.fromTyped(thirdRun).cancel();\n      String thirdRunResult = WorkflowStub.fromTyped(thirdRun).getResult(String.class);\n      System.out.println(\"Third run result: \" + thirdRunResult);\n    } catch (Exception e) {\n      // we are expecting workflow cancelation\n      if (e.getCause() instanceof CanceledFailure) {\n        System.out.println(\"Third run - Workflow execution canceled.\");\n      } else {\n        System.out.println(\"Third run - Workflow exec exception: \" + e.getMessage());\n      }\n    }\n  }\n\n  @SuppressWarnings(\"unused\")\n  private static void fourthRun(WorkflowClient client) {\n    System.out.println(\"\\n\\n**** Fourth Run: cause heartbeat timeout\");\n    // we disable autoheartbeat via env var\n    System.setProperty(\"sample.disableAutoHeartbeat\", \"true\");\n    AutoWorkflow fourth =\n        client.newWorkflowStub(\n            AutoWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    try {\n      String fourthRunResult = fourth.exec(\"Auto heartbeating is cool\");\n      System.out.println(\"Fourth run result: \" + fourthRunResult);\n    } catch (Exception e) {\n      System.out.println(\"Fourth run - Workflow exec exception: \" + e.getClass().getName());\n    }\n  }\n\n  private static void doSleeps(int seconds) {\n    try {\n      Thread.sleep(seconds * 1000L);\n    } catch (Exception e) {\n      System.out.println(e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/activities/AutoActivities.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.activities;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface AutoActivities {\n  String runActivityOne(String input);\n\n  String runActivityTwo(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/activities/AutoActivitiesImpl.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.activities;\n\nimport java.util.concurrent.TimeUnit;\n\npublic class AutoActivitiesImpl implements AutoActivities {\n\n  @Override\n  public String runActivityOne(String input) {\n    return runActivity(\"runActivityOne - \" + input, 10);\n  }\n\n  @Override\n  public String runActivityTwo(String input) {\n    return runActivity(\"runActivityTwo - \" + input, 5);\n  }\n\n  @SuppressWarnings(\"FutureReturnValueIgnored\")\n  private String runActivity(String input, int seconds) {\n    for (int i = 0; i < seconds; i++) {\n      sleep(1);\n    }\n    return \"Activity completed: \" + input;\n  }\n\n  private void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException ee) {\n      // Empty\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/interceptor/AutoHeartbeatActivityInboundCallsInterceptor.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.interceptor;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.client.ActivityCanceledException;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptor;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptorBase;\nimport io.temporal.samples.autoheartbeat.AutoHeartbeatUtil;\nimport java.time.Duration;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ScheduledFuture;\nimport java.util.concurrent.TimeUnit;\n\npublic class AutoHeartbeatActivityInboundCallsInterceptor\n    extends ActivityInboundCallsInterceptorBase {\n  private ActivityExecutionContext activityExecutionContext;\n  private Duration activityHeartbeatTimeout;\n  private AutoHeartbeatUtil autoHeartbeater;\n  private ScheduledFuture scheduledFuture;\n\n  public AutoHeartbeatActivityInboundCallsInterceptor(ActivityInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  @Override\n  public void init(ActivityExecutionContext context) {\n    this.activityExecutionContext = context;\n    activityHeartbeatTimeout = activityExecutionContext.getInfo().getHeartbeatTimeout();\n    super.init(context);\n  }\n\n  @Override\n  @SuppressWarnings({\"FutureReturnValueIgnored\", \"CatchAndPrintStackTrace\"})\n  public ActivityOutput execute(ActivityInput input) {\n    // If activity has heartbeat timeout defined we want to apply auto-heartbeter\n    // Unless we explicitly disabled autoheartbeating via system property\n    if (activityHeartbeatTimeout != null\n        && activityHeartbeatTimeout.getSeconds() > 0\n        && !Boolean.parseBoolean(System.getProperty(\"sample.disableAutoHeartbeat\"))) {\n      System.out.println(\n          \"Auto heartbeating applied for activity: \"\n              + activityExecutionContext.getInfo().getActivityType());\n      autoHeartbeater =\n          new AutoHeartbeatUtil(2, 0, TimeUnit.SECONDS, activityExecutionContext, input);\n      scheduledFuture = autoHeartbeater.start();\n    } else {\n      System.out.println(\n          \"Auto heartbeating not being applied for activity: \"\n              + activityExecutionContext.getInfo().getActivityType());\n    }\n\n    if (scheduledFuture != null) {\n      CompletableFuture activityExecFuture =\n          CompletableFuture.supplyAsync(() -> super.execute(input));\n      CompletableFuture autoHeartbeatFuture =\n          CompletableFuture.supplyAsync(\n              () -> {\n                try {\n                  return scheduledFuture.get();\n                } catch (Exception e) {\n                  throw new ActivityCanceledException(activityExecutionContext.getInfo());\n                }\n              });\n      try {\n        return (ActivityOutput)\n            CompletableFuture.anyOf(autoHeartbeatFuture, activityExecFuture).get();\n      } catch (Exception e) {\n        if (e instanceof ExecutionException) {\n          ExecutionException ee = (ExecutionException) e;\n          if (ee.getCause() instanceof ActivityCanceledException) {\n            throw new ActivityCanceledException(activityExecutionContext.getInfo());\n          }\n        }\n        throw Activity.wrap(e);\n      } finally {\n        if (autoHeartbeater != null) {\n          autoHeartbeater.stop();\n        }\n      }\n    } else {\n      return super.execute(input);\n    }\n  }\n\n  public interface AutoHeartbeaterCancellationCallback {\n    void handle(Exception e);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/interceptor/AutoHeartbeatWorkerInterceptor.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.interceptor;\n\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkerInterceptorBase;\n\npublic class AutoHeartbeatWorkerInterceptor extends WorkerInterceptorBase {\n  @Override\n  public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) {\n    return new AutoHeartbeatActivityInboundCallsInterceptor(next);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/workflows/AutoWorkflow.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.workflows;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface AutoWorkflow {\n  @WorkflowMethod\n  String exec(String input);\n\n  @SignalMethod\n  void cancelActivity();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/autoheartbeat/workflows/AutoWorkflowImpl.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.autoheartbeat.workflows;\n\nimport io.temporal.activity.ActivityCancellationType;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.failure.TimeoutFailure;\nimport io.temporal.samples.autoheartbeat.activities.AutoActivities;\nimport io.temporal.workflow.CancellationScope;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class AutoWorkflowImpl implements AutoWorkflow {\n  private CancellationScope scope;\n\n  @Override\n  public String exec(String input) {\n    AutoActivities activitiesOne =\n        Workflow.newActivityStub(\n            AutoActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(22))\n                .setHeartbeatTimeout(Duration.ofSeconds(8))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                // for sample purposes\n                .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build())\n                .build());\n\n    AutoActivities activitiesTwo =\n        Workflow.newActivityStub(\n            AutoActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(20))\n                .setHeartbeatTimeout(Duration.ofSeconds(7))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                // for sample purposes\n                .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build())\n                .build());\n\n    // Start our activity in CancellationScope so we can cancel it if needed\n    scope =\n        Workflow.newCancellationScope(\n            () -> {\n              activitiesOne.runActivityOne(input);\n              activitiesTwo.runActivityTwo(input);\n            });\n\n    try {\n      scope.run();\n    } catch (ActivityFailure e) {\n      if (e.getCause() instanceof CanceledFailure) {\n        // We dont want workflow to fail in we canceled our scope, just log and return\n        return \"Workflow result after activity cancellation\";\n      } else if (e.getCause() instanceof TimeoutFailure) {\n        return \"Workflow result after activity timeout of type: \"\n            + ((TimeoutFailure) e.getCause()).getTimeoutType().name();\n      } else {\n        throw e;\n      }\n    }\n    return \"completed\";\n  }\n\n  @Override\n  public void cancelActivity() {\n    scope.cancel(\"Canceling scope from signal handler\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchStarter.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport static io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchWorker.TASK_QUEUE;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\n/** Starts a single execution of HeartbeatingActivityBatchWorkflow. */\npublic class HeartbeatingActivityBatchStarter {\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build();\n    HeartbeatingActivityBatchWorkflow batchWorkflow =\n        workflowClient.newWorkflowStub(HeartbeatingActivityBatchWorkflow.class, options);\n    WorkflowExecution execution = WorkflowClient.start(batchWorkflow::processBatch);\n    System.out.println(\n        \"Started batch workflow. WorkflowId=\"\n            + execution.getWorkflowId()\n            + \", RunId=\"\n            + execution.getRunId());\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorker.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\n/**\n * A worker process that hosts implementations of HeartbeatingActivityBatchWorkflow and\n * RecordProcessorActivity.\n */\npublic final class HeartbeatingActivityBatchWorker {\n\n  static final String TASK_QUEUE = \"HeartbeatingActivityBatch\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(HeartbeatingActivityBatchWorkflowImpl.class);\n\n    worker.registerActivitiesImplementations(\n        new RecordProcessorActivityImpl(new RecordLoaderImpl(), new RecordProcessorImpl()));\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorkflow.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HeartbeatingActivityBatchWorkflow {\n\n  /**\n   * Processes the batch of records.\n   *\n   * @return total number of processed records.\n   */\n  @WorkflowMethod\n  int processBatch();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n/**\n * A sample implementation of processing a batch by an activity.\n *\n * <p>An activity can run as long as needed. It reports that it is still alive through heartbeat. If\n * the worker is restarted the activity is retried after the heartbeat timeout. Temporal allows\n * store data in heartbeat _details_. These details are available to the next activity attempt. The\n * progress of the record processing is stored in the details to avoid reprocessing records from the\n * beginning on failures.\n */\npublic final class HeartbeatingActivityBatchWorkflowImpl\n    implements HeartbeatingActivityBatchWorkflow {\n\n  /**\n   * Activity that is used to process batch records. The start-to-close timeout is set to a high\n   * value to support large batch sizes. Heartbeat timeout is required to quickly restart the\n   * activity in case of failures. The heartbeat timeout is also needed to record heartbeat details\n   * at the service.\n   */\n  private final RecordProcessorActivity recordProcessor =\n      Workflow.newActivityStub(\n          RecordProcessorActivity.class,\n          ActivityOptions.newBuilder()\n              .setStartToCloseTimeout(Duration.ofHours(1))\n              .setHeartbeatTimeout(Duration.ofSeconds(10))\n              .build());\n\n  @Override\n  public int processBatch() {\n    // No special logic needed here as activity is retried automatically by the service.\n    return recordProcessor.processRecords();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/README.md",
    "content": "A sample implementation of processing a batch by an Activity.\n\nAn Activity can run as long as needed. \nIt reports that it is still alive through Heartbeat. \n\nIf the Worker is restarted, the Activity is retried after the Heartbeat Timeout. \n\nTemporal allows store data in Heartbeat _details_. \nThese details are available to the next Activity attempt. \nThe progress of the record processing is stored in the details to avoid reprocessing records from the beginning on failures.\n\n#### Running the Iterator Batch Sample\n\nThe sample has two executables. Execute each command in a separate terminal window.\n\nThe first command runs the Worker that hosts the Workflow and Activity Executions. Restart the worker while the batch is\nexecuting to see how activity timeout and retry work.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchWorker\n```\n\nThe second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.heartbeatingactivity.HeartbeatingActivityBatchStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordLoader.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport java.util.Optional;\n\n/**\n * Helper class that is used to iterate over a list of records.\n *\n * <p>A specific implementation depends on a use case. For example, it can execute an SQL DB query\n * or read a comma delimited file. More complex use cases would need passing a different type of\n * offset parameter.\n */\npublic interface RecordLoader {\n\n  /**\n   * Returns the next record.\n   *\n   * @param offset offset of the next record.\n   * @return Record at the offset. Empty optional if offset exceeds the dataset size.\n   */\n  Optional<SingleRecord> getRecord(int offset);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordLoaderImpl.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport java.util.Optional;\n\n/** Fake implementation of RecordLoader. */\npublic final class RecordLoaderImpl implements RecordLoader {\n\n  static final int RECORD_COUNT = 1000;\n\n  @Override\n  public Optional<SingleRecord> getRecord(int offset) {\n    if (offset >= RECORD_COUNT) {\n      return Optional.empty();\n    }\n    return Optional.of(new SingleRecord(offset));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessor.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\n/** A helper class that implements record processing. */\npublic interface RecordProcessor {\n\n  /**\n   * Processes a single record.\n   *\n   * @param record record to process\n   */\n  void processRecord(SingleRecord record);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessorActivity.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface RecordProcessorActivity {\n\n  /** Processes all records in the dataset */\n  int processRecords();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessorActivityImpl.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityExecutionContext;\nimport java.util.Optional;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * RecordProcessorActivity implementation.\n *\n * <p>It relies on RecordLoader to iterate over set of records and process them one by one. The\n * heartbeat is used to remember offset. On activity retry the data from the last recorded heartbeat\n * is used to minimize the number of records that are reprocessed. Note that not every heartbeat\n * call is sent to the service. The frequency of the heartbeat service calls depends on the\n * heartbeat timeout the activity was scheduled with. If no heartbeat timeout is not set then no\n * heartbeat is ever sent to the service.\n *\n * <p>The biggest advantage of this approach is efficiency. It uses very few Temporal resources.\n *\n * <p>The biggest limitation of this approach is that it cannot deal with record processing\n * failures. The only options are either failing the whole batch or skip the record. While it is\n * possible to build additional logic to record failed records somewhere the experience is not\n * seamless.\n */\npublic class RecordProcessorActivityImpl implements RecordProcessorActivity {\n\n  private static final Logger log = LoggerFactory.getLogger(RecordProcessorActivityImpl.class);\n\n  private final RecordLoader recordLoader;\n\n  private final RecordProcessor recordProcessor;\n\n  public RecordProcessorActivityImpl(RecordLoader recordLoader, RecordProcessor recordProcessor) {\n    this.recordLoader = recordLoader;\n    this.recordProcessor = recordProcessor;\n  }\n\n  @Override\n  public int processRecords() {\n    // On activity retry load the last reported offset from the heartbeat details.\n    ActivityExecutionContext context = Activity.getExecutionContext();\n    Optional<Integer> heartbeatDetails = context.getHeartbeatDetails(Integer.class);\n    int offset = heartbeatDetails.orElse(0);\n    log.info(\"Activity processRecords started with offset=\" + offset);\n    // This sample implementation processes records one by one.\n    // If needed it can be changed to use a pool of threads or asynchronous code to process multiple\n    // such records in parallel.\n    while (true) {\n      Optional<SingleRecord> record = recordLoader.getRecord(offset);\n      if (!record.isPresent()) {\n        return offset;\n      }\n      recordProcessor.processRecord(record.get());\n      // Report that activity is still alive. The assumption is that each record takes less time\n      // to process than the heartbeat timeout.\n      // Leverage heartbeat details to record offset.\n      context.heartbeat(offset);\n      offset++;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/RecordProcessorImpl.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/** Fake record processor implementation. */\npublic final class RecordProcessorImpl implements RecordProcessor {\n\n  private static final Logger log = LoggerFactory.getLogger(RecordProcessorImpl.class);\n\n  @Override\n  public void processRecord(SingleRecord record) {\n    // Fake processing logic\n    try {\n      Thread.sleep(100);\n      log.info(\"Processed \" + record);\n    } catch (InterruptedException ignored) {\n      return;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/heartbeatingactivity/SingleRecord.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\n/** Record to process. A real application would add a use case specific data. */\npublic class SingleRecord {\n  private int id;\n\n  public SingleRecord(int id) {\n    this.id = id;\n  }\n\n  /** JSON deserializer needs it */\n  public SingleRecord() {}\n\n  public int getId() {\n    return id;\n  }\n\n  @Override\n  public String toString() {\n    return \"Record{\" + \"id=\" + id + '}';\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchStarter.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport static io.temporal.samples.batch.iterator.IteratorBatchWorker.TASK_QUEUE;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\n/** Starts a single execution of IteratorBatchWorkflow. */\npublic class IteratorBatchStarter {\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build();\n    IteratorBatchWorkflow batchWorkflow =\n        workflowClient.newWorkflowStub(IteratorBatchWorkflow.class, options);\n    WorkflowExecution execution = WorkflowClient.start(batchWorkflow::processBatch, 5, 0);\n    System.out.println(\n        \"Started batch workflow. WorkflowId=\"\n            + execution.getWorkflowId()\n            + \", RunId=\"\n            + execution.getRunId());\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchWorker.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\n/**\n * A worker process that hosts implementations of IteratorBatchWorkflow and RecordProcessorWorkflow\n * as well as RecordLoader activity.\n */\npublic final class IteratorBatchWorker {\n\n  static final String TASK_QUEUE = \"IteratorBatch\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(\n        IteratorBatchWorkflowImpl.class, RecordProcessorWorkflowImpl.class);\n\n    worker.registerActivitiesImplementations(new RecordLoaderImpl());\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchWorkflow.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface IteratorBatchWorkflow {\n\n  /**\n   * Processes the batch of records.\n   *\n   * @param offset the offset of the first record to process. 0 to start the batch processing.\n   * @param pageSize the number of records to process in a single workflow run.\n   * @return total number of processed records.\n   */\n  @WorkflowMethod\n  int processBatch(int pageSize, int offset);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/IteratorBatchWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Implements iterator workflow pattern.\n *\n * <p>A single workflow run processes a single page of records in parallel. Each record is processed\n * using its own RecordProcessorWorkflow child workflow.\n *\n * <p>After all child workflows complete the new run of the parent workflow is created using\n * continue as new. The new run processes the next page of records. This way practically unlimited\n * set of records can be processed.\n */\npublic final class IteratorBatchWorkflowImpl implements IteratorBatchWorkflow {\n\n  private final RecordLoader recordLoader =\n      Workflow.newActivityStub(\n          RecordLoader.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build());\n\n  /** Stub used to continue-as-new. */\n  private final IteratorBatchWorkflow nextRun =\n      Workflow.newContinueAsNewStub(IteratorBatchWorkflow.class);\n\n  @Override\n  public int processBatch(int pageSize, int offset) {\n    // Loads a page of records\n    List<SingleRecord> records = recordLoader.getRecords(pageSize, offset);\n    // Starts a child per record asynchrnously.\n    List<Promise<Void>> results = new ArrayList<>(records.size());\n    for (SingleRecord record : records) {\n      // Uses human friendly child id.\n      String childId = Workflow.getInfo().getWorkflowId() + \"/\" + record.getId();\n      RecordProcessorWorkflow processor =\n          Workflow.newChildWorkflowStub(\n              RecordProcessorWorkflow.class,\n              ChildWorkflowOptions.newBuilder().setWorkflowId(childId).build());\n      Promise<Void> result = Async.procedure(processor::processRecord, record);\n      results.add(result);\n    }\n    // Waits for all children to complete.\n    Promise.allOf(results).get();\n\n    // Skips error handling for the sample brevity.\n    // So failed RecordProcessorWorkflows are ignored.\n\n    // No more records in the dataset. Completes the workflow.\n    if (records.isEmpty()) {\n      return offset;\n    }\n\n    // Continues as new with the increased offset.\n    return nextRun.processBatch(pageSize, offset + records.size());\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/README.md",
    "content": "A sample implementation of the Workflow iterator pattern.\n\nA workflow starts a configured number of Child Workflows in parallel. Each child processes a single record. \nAfter all children complete, the parent calls continue-as-new and starts the children for the next page of records. \n\nThis allows processing a set of records of any size. The advantage of this approach is simplicity. \nThe main disadvantage is that it processes records in batches, with each batch waiting for the slowest child workflow.\n\nA variation of this pattern runs activities instead of child workflows.\n\n#### Running the Iterator Batch Sample\n\nThe sample has two executables. Execute each command in a separate terminal window.\n\nThe first command runs the Worker that hosts the Workflow and Activity Executions.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.iterator.IteratorBatchWorker\n```\n\nThe second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.iterator.IteratorBatchStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/RecordLoader.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.activity.ActivityInterface;\nimport java.util.List;\n\n/**\n * Activity that is used to iterate over a list of records.\n *\n * <p>A specific implementation depends on a use case. For example, it can execute an SQL DB query\n * or read a comma delimited file. More complex use cases would need passing a different type of\n * offset parameter.\n */\n@ActivityInterface\npublic interface RecordLoader {\n\n  /**\n   * Returns the next page of records.\n   *\n   * @param offset offset of the next page.\n   * @param pageSize maximum number of records to return.\n   * @return empty list if no more records to process.\n   */\n  List<SingleRecord> getRecords(int pageSize, int offset);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/RecordLoaderImpl.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Fake implementation of RecordLoader. */\npublic final class RecordLoaderImpl implements RecordLoader {\n\n  // The sample always returns 5 pages.\n  // The real application would iterate over an existing dataset or file.\n  public static final int PAGE_COUNT = 5;\n\n  @Override\n  public List<SingleRecord> getRecords(int pageSize, int offset) {\n    List<SingleRecord> records = new ArrayList<>(pageSize);\n    if (offset < pageSize * PAGE_COUNT) {\n      for (int i = 0; i < pageSize; i++) {\n        records.add(new SingleRecord(offset + i));\n      }\n    }\n    return records;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/RecordProcessorWorkflow.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/** Workflow that implements processing of a single record. */\n@WorkflowInterface\npublic interface RecordProcessorWorkflow {\n\n  /** Processes a single record */\n  @WorkflowMethod\n  void processRecord(SingleRecord r);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/RecordProcessorWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.Random;\nimport org.slf4j.Logger;\n\n/** Fake RecordProcessorWorkflow implementation. */\npublic class RecordProcessorWorkflowImpl implements RecordProcessorWorkflow {\n  public static final Logger log = Workflow.getLogger(RecordProcessorWorkflowImpl.class);\n  private final Random random = Workflow.newRandom();\n\n  @Override\n  public void processRecord(SingleRecord r) {\n    // Simulate some processing\n    Workflow.sleep(Duration.ofSeconds(random.nextInt(30)));\n    log.info(\"Processed \" + r);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/iterator/SingleRecord.java",
    "content": "package io.temporal.samples.batch.iterator;\n\n/** Record to process. A real application would add a use case specific data. */\npublic class SingleRecord {\n  private int id;\n\n  public SingleRecord(int id) {\n    this.id = id;\n  }\n\n  /** JSON deserializer needs it */\n  public SingleRecord() {}\n\n  public int getId() {\n    return id;\n  }\n\n  @Override\n  public String toString() {\n    return \"SingleRecord{\" + \"id=\" + id + '}';\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/BatchProgress.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport java.util.Set;\n\n/** Used as a result of {@link SlidingWindowBatchWorkflow#getProgress()} query. */\npublic final class BatchProgress {\n\n  private final int progress;\n\n  private final Set<Integer> currentRecords;\n\n  public BatchProgress(int progress, Set<Integer> currentRecords) {\n    this.progress = progress;\n    this.currentRecords = currentRecords;\n  }\n\n  /** Count of completed record processing child workflows. */\n  public int getProgress() {\n    return progress;\n  }\n\n  /** Ids of records that are currently being processed by child workflows. */\n  public Set<Integer> getCurrentRecords() {\n    return currentRecords;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/BatchWorkflow.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface BatchWorkflow {\n\n  /**\n   * Processes a batch of records using multiple parallel sliding window workflows.\n   *\n   * @param pageSize the number of records to start processing in a single sliding window workflow\n   *     run.\n   * @param slidingWindowSize the number of records to process in parallel by a single sliding\n   *     window workflow. Can be larger than the pageSize.\n   * @param partitions defines the number of SlidingWindowBatchWorkflows to run in parallel. If\n   *     number of partitions is too low the update rate of a single SlidingWindowBatchWorkflows can\n   *     get too high.\n   * @return total number of processed records.\n   */\n  @WorkflowMethod\n  int processBatch(int pageSize, int slidingWindowSize, int partitions);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/BatchWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Implements BatchWorkflow by running multiple SlidingWindowBatchWorkflows in parallel. */\npublic class BatchWorkflowImpl implements BatchWorkflow {\n\n  private final RecordLoader recordLoader =\n      Workflow.newActivityStub(\n          RecordLoader.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build());\n\n  @Override\n  public int processBatch(int pageSize, int slidingWindowSize, int partitions) {\n    // The sample partitions the data set into continuous ranges.\n    // A real application can choose any other way to divide the records into multiple collections.\n    int totalCount = recordLoader.getRecordCount();\n    int partitionSize = totalCount / partitions + (totalCount % partitions > 0 ? 1 : 0);\n    List<Promise<Integer>> results = new ArrayList<>(partitions);\n    for (int i = 0; i < partitions; i++) {\n      // Makes child id more user-friendly\n      String childId = Workflow.getInfo().getWorkflowId() + \"/\" + i;\n      SlidingWindowBatchWorkflow partitionWorkflow =\n          Workflow.newChildWorkflowStub(\n              SlidingWindowBatchWorkflow.class,\n              ChildWorkflowOptions.newBuilder().setWorkflowId(childId).build());\n      // Define partition boundaries.\n      int offset = partitionSize * i;\n      int maximumOffset = Math.min(offset + partitionSize, totalCount);\n\n      ProcessBatchInput input = new ProcessBatchInput();\n      input.setPageSize(pageSize);\n      input.setSlidingWindowSize(slidingWindowSize);\n      input.setOffset(offset);\n      input.setMaximumOffset(maximumOffset);\n\n      Promise<Integer> partitionResult = Async.function(partitionWorkflow::processBatch, input);\n      results.add(partitionResult);\n    }\n    int result = 0;\n    for (Promise<Integer> partitionResult : results) {\n      result += partitionResult.get();\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/ProcessBatchInput.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport java.util.HashSet;\nimport java.util.Set;\n\n/** Input of {@link SlidingWindowBatchWorkflow#processBatch(ProcessBatchInput)} */\npublic final class ProcessBatchInput {\n  private int pageSize;\n  private int slidingWindowSize;\n\n  int offset;\n\n  private int maximumOffset;\n\n  private int progress;\n\n  private Set<Integer> currentRecords = new HashSet<>();\n\n  /** the number of records to load in a single RecordLoader.getRecords call. */\n  public void setPageSize(int pageSize) {\n    this.pageSize = pageSize;\n  }\n\n  /** the number of parallel record processing child workflows to execute. */\n  public void setSlidingWindowSize(int slidingWindowSize) {\n    this.slidingWindowSize = slidingWindowSize;\n  }\n\n  /** index of the first record to process. 0 to start the batch processing. */\n  public void setOffset(int offset) {\n    this.offset = offset;\n  }\n\n  /** The maximum offset (exclusive) to process by this workflow. */\n  public void setMaximumOffset(int maximumOffset) {\n    this.maximumOffset = maximumOffset;\n  }\n\n  /** Total number of records processed so far by this workflow. */\n  public void setProgress(int progress) {\n    this.progress = progress;\n  }\n\n  /**\n   * Ids of records that are being processed by child workflows.\n   *\n   * <p>This puts a limit on the sliding window size as workflow arguments cannot exceed 2MB in JSON\n   * format. Another practical limit is the number of signals a workflow can handle per second.\n   * Adjust the number of partitions to keep this rate at a reasonable value.\n   */\n  public void setCurrentRecords(Set<Integer> currentRecords) {\n    this.currentRecords = currentRecords;\n  }\n\n  public int getPageSize() {\n    return pageSize;\n  }\n\n  public int getSlidingWindowSize() {\n    return slidingWindowSize;\n  }\n\n  public int getOffset() {\n    return offset;\n  }\n\n  public int getMaximumOffset() {\n    return maximumOffset;\n  }\n\n  public int getProgress() {\n    return progress;\n  }\n\n  public Set<Integer> getCurrentRecords() {\n    return currentRecords;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/README.md",
    "content": "A sample implementation of a batch processing Workflow that maintains a sliding window of record processing Workflows.\n\nA Workflow starts a configured number of Child Workflows in parallel. Each child processes a single record. \nWhen a child completes a new child immediately started. \n\nA Parent Workflow calls continue-as-new after starting a preconfigured number of children. \nA child completion is reported through a Signal as a parent cannot directly wait for a child started by a previous run.\n\nMultiple instances of SlidingWindowBatchWorkflow run in parallel each processing a  subset of records to support higher total rate of processing.\n\n#### Running the Sliding Window Batch Sample\n\nThe sample has two executables. Execute each command in a separate terminal window.\n\nThe first command runs the Worker that hosts the Workflow and Activity Executions.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.slidingwindow.SlidingWindowBatchWorker\n```\n\nNote that `Caused by: io.grpc.StatusRuntimeException: INVALID_ARGUMENT: UnhandledCommand` info messages in the output\nare expected and benign. They ensure that signals are not lost when there is a race condition between workflow calling\ncontinue-as-new and receiving a signal. If these messages appear too frequently consider increasing the number of\npartitions parameter passed to `BatchWorkflow.processBatch`. They will completely disappear\nwhen [Issue 1289](https://github.com/temporalio/temporal/issues/1289) is implemented.\n\nThe second command start the BatchWorkflow Execution. Each time the command runs, it starts a new BatchWorkflow\nExecution.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.batch.slidingwindow.SlidingWindowBatchStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordIterable.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.NoSuchElementException;\nimport org.jetbrains.annotations.NotNull;\n\n/** Iterable implementation that relies on RecordLoader activity. */\npublic class RecordIterable implements Iterable<SingleRecord> {\n\n  /**\n   * Iterator implementation that relies on RecordLoader activity.\n   *\n   * <p>This code assumes that RecordLoader.getRecords never returns a failure to the workflow. The\n   * real production application might make a different design choice.\n   */\n  private class RecordIterator implements Iterator<SingleRecord> {\n\n    /**\n     * The last page of records loaded through RecordLoader activity. The activity returns an empty\n     * page to indicate the end of iteration.\n     */\n    private List<SingleRecord> lastPage;\n\n    /** The offset of the last loaded batch of records. */\n    private int offset;\n\n    /** Index into the last loaded page of the next record to return. */\n    private int index;\n\n    RecordIterator() {\n      this.offset = initialOffset;\n      if (initialOffset > maximumOffset) {\n        this.lastPage = new ArrayList<>();\n      } else {\n        int size = Math.min(pageSize, maximumOffset - offset);\n        this.lastPage = recordLoader.getRecords(size, offset);\n      }\n    }\n\n    @Override\n    public boolean hasNext() {\n      return !lastPage.isEmpty();\n    }\n\n    @Override\n    public SingleRecord next() {\n      int size = lastPage.size();\n      if (size == 0) {\n        throw new NoSuchElementException();\n      }\n      SingleRecord result = lastPage.get(index++);\n      if (size == index) {\n        offset += index;\n        index = 0;\n        lastPage = recordLoader.getRecords(pageSize, offset);\n      }\n      return result;\n    }\n  }\n\n  private final int initialOffset;\n\n  private final int pageSize;\n\n  private final int maximumOffset;\n\n  private final RecordLoader recordLoader =\n      Workflow.newActivityStub(\n          RecordLoader.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build());\n\n  /**\n   * @param pageSize size of a single page to load.\n   * @param initialOffset the initial offset to load records from.\n   * @param maximumOffset the maximum offset (exclusive).\n   */\n  public RecordIterable(int pageSize, int initialOffset, int maximumOffset) {\n    this.pageSize = pageSize;\n    this.initialOffset = initialOffset;\n    this.maximumOffset = maximumOffset;\n  }\n\n  @NotNull\n  @Override\n  public Iterator<SingleRecord> iterator() {\n    return new RecordIterator();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordLoader.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.activity.ActivityInterface;\nimport java.util.List;\n\n@ActivityInterface\npublic interface RecordLoader {\n\n  /**\n   * Returns the next page of records.\n   *\n   * @param offset offset of the next page.\n   * @param pageSize maximum number of records to return.\n   * @return empty list if no more records to process.\n   */\n  List<SingleRecord> getRecords(int pageSize, int offset);\n\n  /**\n   * Returns the total record count.\n   *\n   * <p>Used to divide record ranges among partitions. Some applications might choose a completely\n   * different approach for partitioning the data set.\n   */\n  int getRecordCount();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordLoaderImpl.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport java.util.ArrayList;\nimport java.util.List;\n\n/** Fake loader implementation. The real application would iterate over a dataset or file. */\npublic final class RecordLoaderImpl implements RecordLoader {\n\n  private static final int TOTAL_COUNT = 300;\n\n  @Override\n  public List<SingleRecord> getRecords(int pageSize, int offset) {\n    List<SingleRecord> records = new ArrayList<>(pageSize);\n    if (offset < TOTAL_COUNT) {\n      for (int i = offset; i < Math.min(offset + pageSize, TOTAL_COUNT); i++) {\n        records.add(new SingleRecord(i));\n      }\n    }\n    return records;\n  }\n\n  @Override\n  public int getRecordCount() {\n    return TOTAL_COUNT;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordProcessorWorkflow.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/** Workflow that implements processing of a single record. */\n@WorkflowInterface\npublic interface RecordProcessorWorkflow {\n\n  /**\n   * Processes a single record. Must report completion to a parent through {@link\n   * SlidingWindowBatchWorkflow#reportCompletion(int)}\n   */\n  @WorkflowMethod\n  void processRecord(SingleRecord r);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/RecordProcessorWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.Optional;\nimport java.util.Random;\nimport org.slf4j.Logger;\n\n/** Fake RecordProcessorWorkflow implementation. */\npublic final class RecordProcessorWorkflowImpl implements RecordProcessorWorkflow {\n  public static final Logger log = Workflow.getLogger(RecordProcessorWorkflowImpl.class);\n  private final Random random = Workflow.newRandom();\n\n  @Override\n  public void processRecord(SingleRecord r) {\n    processRecordImpl(r);\n    // This workflow is always expected to have a parent.\n    // But for unit testing it might be useful to skip the notification.\n    Optional<String> parentWorkflowId = Workflow.getInfo().getParentWorkflowId();\n    if (parentWorkflowId.isPresent()) {\n      String parentId = parentWorkflowId.get();\n      SlidingWindowBatchWorkflow parent =\n          Workflow.newExternalWorkflowStub(SlidingWindowBatchWorkflow.class, parentId);\n      // Notify parent about record processing completion\n      parent.reportCompletion(r.getId());\n    }\n  }\n\n  /** Application specific record processing logic goes here. */\n  private void processRecordImpl(SingleRecord r) {\n    // Simulate some processing\n    Workflow.sleep(Duration.ofSeconds(random.nextInt(10)));\n    log.info(\"Processed \" + r);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/SingleRecord.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\n/** Record to process. */\npublic class SingleRecord {\n  private int id;\n\n  public SingleRecord(int id) {\n    this.id = id;\n  }\n\n  /** Needed for JSON deserialization. */\n  public SingleRecord() {}\n\n  public int getId() {\n    return id;\n  }\n\n  @Override\n  public String toString() {\n    return \"SingleRecord{\" + \"id=\" + id + '}';\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchStarter.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport static io.temporal.samples.batch.slidingwindow.SlidingWindowBatchWorker.TASK_QUEUE;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class SlidingWindowBatchStarter {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build();\n    BatchWorkflow batchWorkflow = workflowClient.newWorkflowStub(BatchWorkflow.class, options);\n    WorkflowClient.start(batchWorkflow::processBatch, 10, 25, 3);\n    System.out.println(\"Started batch workflow with 3 partitions\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorker.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\n/** Hosts sliding window batch sample workflow and activity implementations. */\npublic final class SlidingWindowBatchWorker {\n\n  static final String TASK_QUEUE = \"SlidingWindow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(\n        BatchWorkflowImpl.class,\n        SlidingWindowBatchWorkflowImpl.class,\n        RecordProcessorWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new RecordLoaderImpl());\n\n    factory.start();\n\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorkflow.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface SlidingWindowBatchWorkflow {\n\n  /**\n   * Process the batch of records.\n   *\n   * @return total number of processed records.\n   */\n  @WorkflowMethod\n  int processBatch(ProcessBatchInput input);\n\n  @SignalMethod\n  void reportCompletion(int recordId);\n\n  @QueryMethod\n  BatchProgress getProgress();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorkflowImpl.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.ParentClosePolicy;\nimport io.temporal.workflow.*;\nimport java.util.*;\n\n/**\n * Implements batch processing by executing a specified number of workflows in parallel. A new\n * record processing workflow is started when a previously started one completes. The child\n * completion is reported through reportCompletion signal as it is not yet possible to passively\n * wait for a workflow that was started by a previous run.\n *\n * <p>Calls continue-as-new after starting 100 children. Note that the sliding window size can be\n * larger than 100.\n */\npublic final class SlidingWindowBatchWorkflowImpl implements SlidingWindowBatchWorkflow {\n\n  /** Stub used to call continue-as-new. */\n  private final SlidingWindowBatchWorkflow nextRun =\n      Workflow.newContinueAsNewStub(SlidingWindowBatchWorkflow.class);\n\n  /** Contains ids of records that are being processed by child workflows. */\n  private Set<Integer> currentRecords;\n\n  /**\n   * Used to accumulate records to remove for signals delivered before processBatch method started\n   * execution\n   */\n  private Set<Integer> recordsToRemove = new HashSet<>();\n\n  /** Count of completed record processing child workflows. */\n  private int progress;\n\n  /**\n   * @return number of processed records\n   */\n  @Override\n  public int processBatch(ProcessBatchInput input) {\n    WorkflowInfo info = Workflow.getInfo();\n    this.progress = input.getProgress();\n    this.currentRecords = input.getCurrentRecords();\n    // Remove records for signals delivered before the workflow run started.\n    int countBefore = this.currentRecords.size();\n    this.currentRecords.removeAll(recordsToRemove);\n    this.progress += countBefore - this.currentRecords.size();\n\n    int pageSize = input.getPageSize();\n    int offset = input.getOffset();\n    int maximumOffset = input.getMaximumOffset();\n    int slidingWindowSize = input.getSlidingWindowSize();\n\n    Iterable<SingleRecord> records = new RecordIterable(pageSize, offset, maximumOffset);\n    List<Promise<WorkflowExecution>> childrenStartedByThisRun = new ArrayList<>();\n\n    Iterator<SingleRecord> recordIterator = records.iterator();\n    while (true) {\n      // After starting slidingWindowSize children blocks until a completion signal is received.\n      Workflow.await(() -> currentRecords.size() < slidingWindowSize);\n      // Completes workflow, if no more records to process.\n      if (!recordIterator.hasNext()) {\n        // Awaits for all children to complete\n        Workflow.await(() -> currentRecords.size() == 0);\n        return offset + childrenStartedByThisRun.size();\n      }\n      SingleRecord record = recordIterator.next();\n\n      // Uses ParentClosePolicy ABANDON to ensure that children survive continue-as-new of a parent.\n      // Assigns user-friendly child workflow id.\n      ChildWorkflowOptions childWorkflowOptions =\n          ChildWorkflowOptions.newBuilder()\n              .setParentClosePolicy(ParentClosePolicy.PARENT_CLOSE_POLICY_ABANDON)\n              .setWorkflowId(info.getWorkflowId() + \"/\" + record.getId())\n              .build();\n\n      RecordProcessorWorkflow processor =\n          Workflow.newChildWorkflowStub(RecordProcessorWorkflow.class, childWorkflowOptions);\n      // Starts a child workflow asynchronously ignoring its result.\n      // The assumption is that the parent workflow doesn't need to deal with child workflow\n      // results and failures. Another assumption is that a child in any situation calls\n      // the reportCompletion signal.\n      Async.procedure(processor::processRecord, record);\n      // Resolves when a child reported successful start.\n      // Used to wait for a child start on continue-as-new.\n      Promise<WorkflowExecution> childStartedPromise = Workflow.getWorkflowExecution(processor);\n      childrenStartedByThisRun.add(childStartedPromise);\n      currentRecords.add(record.getId());\n      // Continues-as-new after starting pageSize children\n      if (childrenStartedByThisRun.size() == pageSize) {\n        // Waits for all children to start. Without this wait, workflow completion through\n        // continue-as-new might lead to a situation when they never start.\n        // Assumes that they never fail to start as their automatically generated\n        // IDs are not expected to collide.\n        Promise.allOf(childrenStartedByThisRun).get();\n        // Continues as new to keep the history size bounded\n        ProcessBatchInput newInput = new ProcessBatchInput();\n        newInput.setPageSize(pageSize);\n        newInput.setSlidingWindowSize(slidingWindowSize);\n        newInput.setOffset(offset + childrenStartedByThisRun.size());\n        newInput.setMaximumOffset(maximumOffset);\n        newInput.setProgress(progress);\n        newInput.setCurrentRecords(currentRecords);\n        return nextRun.processBatch(newInput);\n      }\n    }\n  }\n\n  @Override\n  public void reportCompletion(int recordId) {\n    // Handle situation when signal handler is called before the workflow main method.\n    if (currentRecords == null) {\n      recordsToRemove.add(recordId);\n      return;\n    }\n    // Dedupes signals as in some edge cases they can be duplicated.\n    if (currentRecords.remove(recordId)) {\n      progress++;\n    }\n  }\n\n  @Override\n  public BatchProgress getProgress() {\n    return new BatchProgress(progress, currentRecords);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/Booking.java",
    "content": "package io.temporal.samples.bookingsaga;\n\npublic final class Booking {\n  private String carReservationID;\n  private String hotelReservationID;\n  private String flightReservationID;\n\n  /** Empty constructor to keep Jackson serializer happy. */\n  public Booking() {}\n\n  public Booking(String carReservationID, String hotelReservationID, String flightReservationID) {\n    this.carReservationID = carReservationID;\n    this.hotelReservationID = hotelReservationID;\n    this.flightReservationID = flightReservationID;\n  }\n\n  public String getCarReservationID() {\n    return carReservationID;\n  }\n\n  public String getHotelReservationID() {\n    return hotelReservationID;\n  }\n\n  public String getFlightReservationID() {\n    return flightReservationID;\n  }\n\n  @Override\n  public String toString() {\n    return \"Booking{\"\n        + \"carReservationID='\"\n        + carReservationID\n        + '\\''\n        + \", hotelReservationID='\"\n        + hotelReservationID\n        + '\\''\n        + \", flightReservationID='\"\n        + flightReservationID\n        + '\\''\n        + '}';\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/README.md",
    "content": "## Saga example: trip booking\n\nTemporal implementation of\nthe [Camunda BPMN trip booking example](https://github.com/berndruecker/trip-booking-saga-java) which demonstrates\nTemporal approach to SAGA.\n\nRun the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.bookingsaga.TripBookingSaga\n```\n\nNote that the booking is expected to fail to demonstrate the compensation flow.\n\nSample unit\ntesting: [TripBookingWorkflowTest](https://github.com/temporalio/samples-java/blob/main/core/src/test/java/io/temporal/samples/bookingsaga/TripBookingWorkflowTest.java)\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingActivities.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface TripBookingActivities {\n\n  /**\n   * Request a car rental reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String reserveCar(String requestId, String name);\n\n  /**\n   * Request a flight reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String bookFlight(String requestId, String name);\n\n  /**\n   * Request a hotel reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String bookHotel(String requestId, String name);\n\n  /**\n   * Cancel a flight reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to bookFlight\n   * @return cancellationConfirmationID\n   */\n  String cancelFlight(String requestId, String name);\n\n  /**\n   * Cancel a hotel reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to bookHotel\n   * @return cancellationConfirmationID\n   */\n  String cancelHotel(String requestId, String name);\n\n  /**\n   * Cancel a car rental reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to reserveCar\n   * @return cancellationConfirmationID\n   */\n  String cancelCar(String requestId, String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingActivitiesImpl.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport io.temporal.failure.ApplicationFailure;\nimport java.util.UUID;\n\npublic class TripBookingActivitiesImpl implements TripBookingActivities {\n  @Override\n  public String reserveCar(String requestId, String name) {\n    System.out.println(\"reserving car for request '\" + requestId + \"` and name `\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String bookFlight(String requestId, String name) {\n    System.out.println(\n        \"failing to book flight for request '\" + requestId + \"' and name '\" + name + \"'\");\n    throw ApplicationFailure.newNonRetryableFailure(\n        \"Flight booking did not work\", \"bookingFailure\");\n  }\n\n  @Override\n  public String bookHotel(String requestId, String name) {\n    System.out.println(\"booking hotel for request '\" + requestId + \"` and name `\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelFlight(String requestId, String name) {\n    System.out.println(\"cancelling flight reservation '\" + requestId + \"' for '\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelHotel(String requestId, String name) {\n    System.out.println(\"cancelling hotel reservation '\" + requestId + \"' for '\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelCar(String requestId, String name) {\n    System.out.println(\"cancelling car reservation '\" + requestId + \"' for '\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingClient.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport com.google.common.base.Throwables;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class TripBookingClient {\n\n  static final String TASK_QUEUE = \"TripBooking\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(\"Booking1\").build();\n    TripBookingWorkflow trip = client.newWorkflowStub(TripBookingWorkflow.class, options);\n    try {\n      Booking booking = trip.bookTrip(\"trip1\");\n      System.out.println(\"Booking: \" + booking);\n    } catch (Exception e) {\n      System.out.println(Throwables.getStackTraceAsString(e));\n    }\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingWorker.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class TripBookingWorker {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    Worker worker = factory.newWorker(TripBookingClient.TASK_QUEUE);\n\n    // Workflows are stateful. So you need a type to create instances.\n    worker.registerWorkflowImplementationTypes(TripBookingWorkflowImpl.class);\n\n    // Activities are stateless and thread safe. So a shared instance is used.\n    TripBookingActivities tripBookingActivities = new TripBookingActivitiesImpl();\n    worker.registerActivitiesImplementations(tripBookingActivities);\n\n    // Start all workers created by this factory.\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TripBookingClient.TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingWorkflow.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface TripBookingWorkflow {\n  @WorkflowMethod\n  Booking bookTrip(String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsaga/TripBookingWorkflowImpl.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.workflow.Saga;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class TripBookingWorkflowImpl implements TripBookingWorkflow {\n\n  private final ActivityOptions options =\n      ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build();\n  private final TripBookingActivities activities =\n      Workflow.newActivityStub(TripBookingActivities.class, options);\n\n  @Override\n  public Booking bookTrip(String name) {\n    // Configure SAGA to run compensation activities in parallel\n    Saga.Options sagaOptions = new Saga.Options.Builder().setParallelCompensation(true).build();\n    Saga saga = new Saga(sagaOptions);\n    try {\n      // addCompensation is added before the actual call to handle situations when the call failed\n      // due to a timeout and its success is not clear.\n      // The compensation code must handle situations when the actual function wasn't executed\n      // gracefully.\n      String carReservationRequestId = Workflow.randomUUID().toString();\n      saga.addCompensation(activities::cancelCar, carReservationRequestId, name);\n      String carReservationID = activities.reserveCar(carReservationRequestId, name);\n\n      String hotelReservationRequestID = Workflow.randomUUID().toString();\n      saga.addCompensation(activities::cancelHotel, hotelReservationRequestID, name);\n      String hotelReservationId = activities.bookHotel(hotelReservationRequestID, name);\n\n      String flightReservationRequestID = Workflow.randomUUID().toString();\n      saga.addCompensation(activities::cancelFlight, flightReservationRequestID, name);\n      String flightReservationID = activities.bookFlight(flightReservationRequestID, name);\n      return new Booking(carReservationID, hotelReservationId, flightReservationID);\n    } catch (ActivityFailure e) {\n      // Ensure that compensations are executed even if the workflow is canceled.\n      Workflow.newDetachedCancellationScope(() -> saga.compensate()).run();\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/Booking.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\npublic final class Booking {\n  private final String carReservationID;\n  private final String hotelReservationID;\n  private final String flightReservationID;\n\n  public Booking(String carReservationID, String hotelReservationID, String flightReservationID) {\n    this.carReservationID = carReservationID;\n    this.hotelReservationID = hotelReservationID;\n    this.flightReservationID = flightReservationID;\n  }\n\n  public String getCarReservationID() {\n    return carReservationID;\n  }\n\n  public String getHotelReservationID() {\n    return hotelReservationID;\n  }\n\n  public String getFlightReservationID() {\n    return flightReservationID;\n  }\n\n  @Override\n  public String toString() {\n    return \"Booking{\"\n        + \"carReservationID='\"\n        + carReservationID\n        + '\\''\n        + \", hotelReservationID='\"\n        + hotelReservationID\n        + '\\''\n        + \", flightReservationID='\"\n        + flightReservationID\n        + '\\''\n        + '}';\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/README.md",
    "content": "## Saga example: synchronous trip booking\n\nThe sample demonstrates low latency workflow with client synchronously waiting for result using an update.\nIn case of failures the caller is unblocked and workflow continues executing compensations\nfor as long as needed.\n\nRun the following command to start the worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.bookingsyncsaga.TripBookingWorker\n```\n\nRun the following command to request a booking.\nNote that the booking is expected to fail to demonstrate the compensation flow.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.bookingsyncsaga.TripBookingClient\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingActivities.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface TripBookingActivities {\n\n  /**\n   * Request a car rental reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String reserveCar(String requestId, String name);\n\n  /**\n   * Request a flight reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String bookFlight(String requestId, String name);\n\n  /**\n   * Request a hotel reservation.\n   *\n   * @param requestId used for idempotency and compensation correlation.\n   * @param name customer name\n   * @return reservationID\n   */\n  String bookHotel(String requestId, String name);\n\n  /**\n   * Cancel a flight reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to bookFlight\n   * @return cancellationConfirmationID\n   */\n  String cancelFlight(String requestId, String name);\n\n  /**\n   * Cancel a hotel reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to bookHotel\n   * @return cancellationConfirmationID\n   */\n  String cancelHotel(String requestId, String name);\n\n  /**\n   * Cancel a car rental reservation.\n   *\n   * @param name customer name\n   * @param requestId the same id is passed to reserveCar\n   * @return cancellationConfirmationID\n   */\n  String cancelCar(String requestId, String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingActivitiesImpl.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport io.temporal.failure.ApplicationFailure;\nimport java.util.UUID;\n\npublic class TripBookingActivitiesImpl implements TripBookingActivities {\n  @Override\n  public String reserveCar(String requestId, String name) {\n    System.out.println(\"reserving car for request '\" + requestId + \"` and name `\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String bookFlight(String requestId, String name) {\n    System.out.println(\n        \"failing to book flight for request '\" + requestId + \"' and name '\" + name + \"'\");\n    throw ApplicationFailure.newNonRetryableFailure(\n        \"Flight booking did not work\", \"bookingFailure\");\n  }\n\n  @Override\n  public String bookHotel(String requestId, String name) {\n    System.out.println(\"booking hotel for request '\" + requestId + \"` and name `\" + name + \"'\");\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelFlight(String requestId, String name) {\n    System.out.println(\"cancelling flight reservation '\" + requestId + \"' for '\" + name + \"'\");\n    sleep(1000);\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelHotel(String requestId, String name) {\n    System.out.println(\"cancelling hotel reservation '\" + requestId + \"' for '\" + name + \"'\");\n    sleep(1000);\n    return UUID.randomUUID().toString();\n  }\n\n  @Override\n  public String cancelCar(String requestId, String name) {\n    System.out.println(\"cancelling car reservation '\" + requestId + \"' for '\" + name + \"'\");\n    sleep(1000);\n    return UUID.randomUUID().toString();\n  }\n\n  private static void sleep(long milliseconds) {\n    try {\n      Thread.sleep(milliseconds);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingClient.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport com.google.common.base.Throwables;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class TripBookingClient {\n\n  static final String TASK_QUEUE = \"TripBookingSync\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(\"Booking1\").build();\n    TripBookingWorkflow trip1 = client.newWorkflowStub(TripBookingWorkflow.class, options);\n    // Start workflow asynchronously\n    WorkflowClient.start(trip1::bookTrip, \"trip1\");\n    try {\n      // Wait for workflow to complete or fail the booking using an update.\n      Booking booking = trip1.waitForBooking();\n      System.out.println(\"Booking: \" + booking);\n    } catch (Exception e) {\n      System.out.println(Throwables.getStackTraceAsString(e));\n    }\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingWorker.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class TripBookingWorker {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // gRPC stubs wrapper that talks to the local docker instance of temporal service.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    Worker worker = factory.newWorker(TripBookingClient.TASK_QUEUE);\n\n    // Workflows are stateful. So you need a type to create instances.\n    worker.registerWorkflowImplementationTypes(TripBookingWorkflowImpl.class);\n\n    // Activities are stateless and thread safe. So a shared instance is used.\n    TripBookingActivities tripBookingActivities = new TripBookingActivitiesImpl();\n    worker.registerActivitiesImplementations(tripBookingActivities);\n\n    // Start all workers created by this factory.\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TripBookingClient.TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingWorkflow.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface TripBookingWorkflow {\n  @WorkflowMethod\n  void bookTrip(String name);\n\n  /**\n   * Used to wait for booking completion or failure. After this method returns a failure workflow\n   * keeps running executing compensations.\n   *\n   * @return booking information.\n   */\n  @UpdateMethod\n  Booking waitForBooking();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/bookingsyncsaga/TripBookingWorkflowImpl.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.activity.LocalActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.workflow.CompletablePromise;\nimport io.temporal.workflow.Saga;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class TripBookingWorkflowImpl implements TripBookingWorkflow {\n\n  /**\n   * Use local activities for the happy path. This allows to execute the whole sequence as a single\n   * workflow task. Don't use local activities if you expect long retries.\n   */\n  private final LocalActivityOptions options =\n      LocalActivityOptions.newBuilder()\n          .build()\n          .newBuilder()\n          .setStartToCloseTimeout(Duration.ofSeconds(1))\n          .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build())\n          .build();\n\n  private final TripBookingActivities activities =\n      Workflow.newLocalActivityStub(TripBookingActivities.class, options);\n\n  /** Use normal activities for compensations, as they potentially need long retries. */\n  private final ActivityOptions compensationOptions =\n      ActivityOptions.newBuilder()\n          .setStartToCloseTimeout(Duration.ofHours(1))\n          .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())\n          .build();\n\n  private final TripBookingActivities compensationActivities =\n      Workflow.newActivityStub(TripBookingActivities.class, compensationOptions);\n\n  /** Used to pass result to the update function. */\n  private final CompletablePromise<Booking> booking = Workflow.newPromise();\n\n  @Override\n  public void bookTrip(String name) {\n    Saga.Options sagaOptions = new Saga.Options.Builder().build();\n    Saga saga = new Saga(sagaOptions);\n    try {\n      // addCompensation is added before the actual call to handle situations when the call failed\n      // due to\n      // a timeout and its success is not clear.\n      // The compensation code must handle situations when the actual function wasn't executed\n      // gracefully.\n      String carReservationRequestId = Workflow.randomUUID().toString();\n      saga.addCompensation(compensationActivities::cancelCar, carReservationRequestId, name);\n      String carReservationID = activities.reserveCar(carReservationRequestId, name);\n\n      String hotelReservationRequestID = Workflow.randomUUID().toString();\n      saga.addCompensation(compensationActivities::cancelHotel, hotelReservationRequestID, name);\n      String hotelReservationId = activities.bookHotel(hotelReservationRequestID, name);\n\n      String flightReservationRequestID = Workflow.randomUUID().toString();\n      saga.addCompensation(compensationActivities::cancelFlight, flightReservationRequestID, name);\n      String flightReservationID = activities.bookFlight(flightReservationRequestID, name);\n\n      // Unblock the update function\n      booking.complete(new Booking(carReservationID, hotelReservationId, flightReservationID));\n    } catch (ActivityFailure e) {\n      // Unblock the update function\n      booking.completeExceptionally(e);\n      // Ensure that compensations are executed even if the workflow is canceled.\n      Workflow.newDetachedCancellationScope(() -> saga.compensate()).run();\n      throw e;\n    }\n  }\n\n  @Override\n  public Booking waitForBooking() {\n    return booking.get();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/common/QueryWorkflowExecution.java",
    "content": "package io.temporal.samples.common;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.util.Optional;\n\n/**\n * Queries a workflow execution using the Temporal query API. Temporal redirects a query to any\n * currently running workflow worker for the workflow type of the requested workflow execution.\n *\n * @author fateev\n */\npublic class QueryWorkflowExecution {\n\n  public static void main(String[] args) {\n    if (args.length < 2 || args.length > 3) {\n      System.err.println(\n          \"Usage: java \"\n              + QueryWorkflowExecution.class.getName()\n              + \" <queryType> <workflowId> [<runId>]\");\n      System.exit(1);\n    }\n    String queryType = args[0];\n    String workflowId = args[1];\n    String runId = args.length == 3 ? args[2] : \"\";\n\n    // gRPC stubs wrapper that talks to the local docker instance of temporal service.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkflowExecution workflowExecution =\n        WorkflowExecution.newBuilder().setWorkflowId(workflowId).setRunId(runId).build();\n    WorkflowStub workflow = client.newUntypedWorkflowStub(workflowExecution, Optional.empty());\n\n    String result = workflow.query(queryType, String.class);\n\n    System.out.println(\"Query result for \" + workflowExecution + \":\");\n    System.out.println(result);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/ClientCounter.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport java.util.AbstractMap;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/** Simple counter class. */\npublic class ClientCounter {\n  private static final String NUM_OF_GET_RESULT = \"numOfGetResult\";\n  private static final String NUM_OF_WORKFLOW_EXECUTIONS = \"numOfWorkflowExec\";\n  private static final String NUM_OF_SIGNALS = \"numOfSignals\";\n  private static final String NUM_OF_QUERIES = \"numOfQueries\";\n  private static final Map<String, Map<String, Integer>> perWorkflowIdMap =\n      Collections.synchronizedMap(new HashMap<>());\n\n  public String getInfo() {\n    StringBuilder stringBuilder = new StringBuilder();\n    for (String workflowRunId : perWorkflowIdMap.keySet()) {\n      stringBuilder.append(\"\\n** Workflow ID: \" + workflowRunId);\n      Map<String, Integer> info = perWorkflowIdMap.get(workflowRunId);\n      stringBuilder.append(\n          \"\\n\\tTotal Number of Workflow Exec: \" + info.get(NUM_OF_WORKFLOW_EXECUTIONS));\n      stringBuilder.append(\"\\n\\tTotal Number of Signals: \" + info.get(NUM_OF_SIGNALS));\n      stringBuilder.append(\"\\n\\tTotal Number of Queries: \" + info.get(NUM_OF_QUERIES));\n      stringBuilder.append(\"\\n\\tTotal Number of GetResult: \" + info.get(NUM_OF_GET_RESULT));\n    }\n\n    return stringBuilder.toString();\n  }\n\n  private void add(String workflowId, String type) {\n    if (!perWorkflowIdMap.containsKey(workflowId)) {\n      perWorkflowIdMap.put(workflowId, getDefaultInfoMap());\n    }\n\n    if (perWorkflowIdMap.get(workflowId).get(type) == null) {\n      perWorkflowIdMap.get(workflowId).put(type, 1);\n    } else {\n      int current = perWorkflowIdMap.get(workflowId).get(type).intValue();\n      int next = current + 1;\n      perWorkflowIdMap.get(workflowId).put(type, next);\n    }\n  }\n\n  public int getNumOfWorkflowExecutions(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_WORKFLOW_EXECUTIONS);\n  }\n\n  public int getNumOfGetResults(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_GET_RESULT);\n  }\n\n  public int getNumOfSignals(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_SIGNALS);\n  }\n\n  public int getNumOfQueries(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_QUERIES);\n  }\n\n  /**\n   * Creates a default counter info map for a workflowid\n   *\n   * @return default counter info map\n   */\n  private Map<String, Integer> getDefaultInfoMap() {\n    return Stream.of(\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_WORKFLOW_EXECUTIONS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_SIGNALS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_GET_RESULT, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_QUERIES, 0))\n        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n  }\n\n  public void addStartInvocation(String workflowId) {\n    add(workflowId, NUM_OF_WORKFLOW_EXECUTIONS);\n  }\n\n  public void addSignalInvocation(String workflowId) {\n    add(workflowId, NUM_OF_SIGNALS);\n  }\n\n  public void addGetResultInvocation(String workflowId) {\n    add(workflowId, NUM_OF_GET_RESULT);\n  }\n\n  public void addQueryInvocation(String workflowId) {\n    add(workflowId, NUM_OF_QUERIES);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/InterceptorStarter.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.common.interceptors.WorkflowClientInterceptor;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.countinterceptor.activities.MyActivitiesImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyChildWorkflowImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflow;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflowImpl;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class InterceptorStarter {\n\n  public static SimpleCountWorkerInterceptor workerInterceptor = new SimpleCountWorkerInterceptor();\n  private static final String TEST_QUEUE = \"test-queue\";\n  private static final String WORKFLOW_ID = \"TestInterceptorWorkflow\";\n\n  private static final Logger logger = LoggerFactory.getLogger(SimpleCountWorkerInterceptor.class);\n\n  public static void main(String[] args) {\n\n    final ClientCounter clientCounter = new ClientCounter();\n    final WorkflowClientInterceptor clientInterceptor = new SimpleClientInterceptor(clientCounter);\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service, WorkflowClientOptions.newBuilder().setInterceptors(clientInterceptor).build());\n\n    WorkerFactoryOptions wfo =\n        WorkerFactoryOptions.newBuilder()\n            .setWorkerInterceptors(workerInterceptor)\n            .validateAndBuildWithDefaults();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client, wfo);\n\n    Worker worker = factory.newWorker(TEST_QUEUE);\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class, MyChildWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new MyActivitiesImpl());\n    factory.start();\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TEST_QUEUE).build();\n\n    MyWorkflow workflow = client.newWorkflowStub(MyWorkflow.class, workflowOptions);\n\n    WorkflowClient.start(workflow::exec);\n\n    workflow.signalNameAndTitle(\"John\", \"Customer\");\n\n    String name = workflow.queryName();\n    String title = workflow.queryTitle();\n\n    // Send exit signal to workflow\n    workflow.exit();\n\n    // Wait for workflow completion via WorkflowStub\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    String result = untyped.getResult(String.class);\n\n    // Print workflow\n    logger.info(\"Workflow Result: \" + result);\n\n    // Print the Query results\n    logger.info(\"Query results: \");\n    logger.info(\"Name: \" + name);\n    logger.info(\"Title: \" + title);\n\n    // Print the Worker Counter Info\n    logger.info(\"Collected Worker Counter Info: \");\n    logger.info(WorkerCounter.getInfo());\n\n    // Print the Client Counter Info\n    logger.info(\"Collected Client Counter Info: \");\n    logger.info(clientCounter.getInfo());\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/README.md",
    "content": "# Demo Workflow Interceptor\n\nThe sample demonstrates: \n- 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.\n- 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.\n\nRun the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.countinterceptor.InterceptorStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleClientCallsInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.common.interceptors.WorkflowClientCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowClientCallsInterceptorBase;\nimport java.util.concurrent.TimeoutException;\n\npublic class SimpleClientCallsInterceptor extends WorkflowClientCallsInterceptorBase {\n  private ClientCounter clientCounter;\n\n  public SimpleClientCallsInterceptor(\n      WorkflowClientCallsInterceptor next, ClientCounter clientCounter) {\n    super(next);\n    this.clientCounter = clientCounter;\n  }\n\n  @Override\n  public WorkflowStartOutput start(WorkflowStartInput input) {\n    clientCounter.addStartInvocation(input.getWorkflowId());\n    return super.start(input);\n  }\n\n  @Override\n  public WorkflowSignalOutput signal(WorkflowSignalInput input) {\n    clientCounter.addSignalInvocation(input.getWorkflowExecution().getWorkflowId());\n    return super.signal(input);\n  }\n\n  @Override\n  public <R> GetResultOutput<R> getResult(GetResultInput<R> input) throws TimeoutException {\n    clientCounter.addGetResultInvocation(input.getWorkflowExecution().getWorkflowId());\n    return super.getResult(input);\n  }\n\n  @Override\n  public <R> QueryOutput<R> query(QueryInput<R> input) {\n    clientCounter.addQueryInvocation(input.getWorkflowExecution().getWorkflowId());\n    return super.query(input);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleClientInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.common.interceptors.WorkflowClientCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowClientInterceptorBase;\n\npublic class SimpleClientInterceptor extends WorkflowClientInterceptorBase {\n\n  private ClientCounter clientCounter;\n\n  public SimpleClientInterceptor(ClientCounter clientCounter) {\n    this.clientCounter = clientCounter;\n  }\n\n  @Override\n  public WorkflowClientCallsInterceptor workflowClientCallsInterceptor(\n      WorkflowClientCallsInterceptor next) {\n    return new SimpleClientCallsInterceptor(next, clientCounter);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountActivityInboundCallsInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptor;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptorBase;\n\npublic class SimpleCountActivityInboundCallsInterceptor\n    extends ActivityInboundCallsInterceptorBase {\n\n  private ActivityExecutionContext activityExecutionContext;\n\n  public SimpleCountActivityInboundCallsInterceptor(ActivityInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  @Override\n  public void init(ActivityExecutionContext context) {\n    this.activityExecutionContext = context;\n    super.init(context);\n  }\n\n  @Override\n  public ActivityOutput execute(ActivityInput input) {\n    WorkerCounter.add(\n        this.activityExecutionContext.getInfo().getWorkflowId(),\n        WorkerCounter.NUM_OF_ACTIVITY_EXECUTIONS);\n    return super.execute(input);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountWorkerInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.common.interceptors.*;\n\npublic class SimpleCountWorkerInterceptor extends WorkerInterceptorBase {\n\n  @Override\n  public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) {\n    return new SimpleCountWorkflowInboundCallsInterceptor(next);\n  }\n\n  @Override\n  public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) {\n    return new SimpleCountActivityInboundCallsInterceptor(next);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountWorkflowInboundCallsInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptorBase;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInfo;\n\npublic class SimpleCountWorkflowInboundCallsInterceptor\n    extends WorkflowInboundCallsInterceptorBase {\n\n  private WorkflowInfo workflowInfo;\n\n  public SimpleCountWorkflowInboundCallsInterceptor(WorkflowInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  @Override\n  public void init(WorkflowOutboundCallsInterceptor outboundCalls) {\n    this.workflowInfo = Workflow.getInfo();\n    super.init(new SimpleCountWorkflowOutboundCallsInterceptor(outboundCalls));\n  }\n\n  @Override\n  public WorkflowOutput execute(WorkflowInput input) {\n    WorkerCounter.add(this.workflowInfo.getWorkflowId(), WorkerCounter.NUM_OF_WORKFLOW_EXECUTIONS);\n    return super.execute(input);\n  }\n\n  @Override\n  public void handleSignal(SignalInput input) {\n    WorkerCounter.add(this.workflowInfo.getWorkflowId(), WorkerCounter.NUM_OF_SIGNALS);\n    super.handleSignal(input);\n  }\n\n  @Override\n  public QueryOutput handleQuery(QueryInput input) {\n    WorkerCounter.add(this.workflowInfo.getWorkflowId(), WorkerCounter.NUM_OF_QUERIES);\n    return super.handleQuery(input);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/SimpleCountWorkflowOutboundCallsInterceptor.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptorBase;\nimport io.temporal.workflow.Workflow;\n\npublic class SimpleCountWorkflowOutboundCallsInterceptor\n    extends WorkflowOutboundCallsInterceptorBase {\n\n  public SimpleCountWorkflowOutboundCallsInterceptor(WorkflowOutboundCallsInterceptor next) {\n    super(next);\n  }\n\n  @Override\n  public <R> ChildWorkflowOutput<R> executeChildWorkflow(ChildWorkflowInput<R> input) {\n    WorkerCounter.add(\n        Workflow.getInfo().getWorkflowId(), WorkerCounter.NUM_OF_CHILD_WORKFLOW_EXECUTIONS);\n    return super.executeChildWorkflow(input);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/WorkerCounter.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport java.util.AbstractMap;\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\n/**\n * Simple counter class. Static impl just for the sake of the sample. Note: in your applications you\n * should use CDI for example instead.\n */\npublic class WorkerCounter {\n  private static Map<String, Map<String, Integer>> perWorkflowIdMap =\n      Collections.synchronizedMap(new HashMap<>());\n\n  public static final String NUM_OF_WORKFLOW_EXECUTIONS = \"numOfWorkflowExec\";\n  public static final String NUM_OF_CHILD_WORKFLOW_EXECUTIONS = \"numOfChildWorkflowExec\";\n  public static final String NUM_OF_ACTIVITY_EXECUTIONS = \"numOfActivityExec\";\n  public static final String NUM_OF_SIGNALS = \"numOfSignals\";\n  public static final String NUM_OF_QUERIES = \"numOfQueries\";\n\n  public static void add(String workflowId, String type) {\n    if (!perWorkflowIdMap.containsKey(workflowId)) {\n      perWorkflowIdMap.put(workflowId, getDefaultInfoMap());\n    }\n\n    if (perWorkflowIdMap.get(workflowId).get(type) == null) {\n      perWorkflowIdMap.get(workflowId).put(type, 1);\n    } else {\n      int current = perWorkflowIdMap.get(workflowId).get(type).intValue();\n      int next = current + 1;\n      perWorkflowIdMap.get(workflowId).put(type, next);\n    }\n  }\n\n  public static int getNumOfWorkflowExecutions(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_WORKFLOW_EXECUTIONS);\n  }\n\n  public static int getNumOfChildWorkflowExecutions(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_CHILD_WORKFLOW_EXECUTIONS);\n  }\n\n  public static int getNumOfActivityExecutions(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_ACTIVITY_EXECUTIONS);\n  }\n\n  public static int getNumOfSignals(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_SIGNALS);\n  }\n\n  public static int getNumOfQueries(String workflowId) {\n    return perWorkflowIdMap.get(workflowId).get(NUM_OF_QUERIES);\n  }\n\n  public static String getInfo() {\n    StringBuilder stringBuilder = new StringBuilder();\n    for (String workflowRunId : perWorkflowIdMap.keySet()) {\n      stringBuilder.append(\"\\n** Workflow ID: \" + workflowRunId);\n      Map<String, Integer> info = perWorkflowIdMap.get(workflowRunId);\n      stringBuilder.append(\n          \"\\n\\tTotal Number of Workflow Exec: \" + info.get(NUM_OF_WORKFLOW_EXECUTIONS));\n      stringBuilder.append(\n          \"\\n\\tTotal Number of Child Workflow Exec: \" + info.get(NUM_OF_CHILD_WORKFLOW_EXECUTIONS));\n      stringBuilder.append(\n          \"\\n\\tTotal Number of Activity Exec: \" + info.get(NUM_OF_ACTIVITY_EXECUTIONS));\n      stringBuilder.append(\"\\n\\tTotal Number of Signals: \" + info.get(NUM_OF_SIGNALS));\n      stringBuilder.append(\"\\n\\tTotal Number of Queries: \" + info.get(NUM_OF_QUERIES));\n    }\n\n    return stringBuilder.toString();\n  }\n\n  /**\n   * Creates a default counter info map for a workflowid\n   *\n   * @return default counter info map\n   */\n  private static Map<String, Integer> getDefaultInfoMap() {\n    return Stream.of(\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_WORKFLOW_EXECUTIONS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_CHILD_WORKFLOW_EXECUTIONS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_ACTIVITY_EXECUTIONS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_SIGNALS, 0),\n            new AbstractMap.SimpleImmutableEntry<>(NUM_OF_QUERIES, 0))\n        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/activities/MyActivities.java",
    "content": "package io.temporal.samples.countinterceptor.activities;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface MyActivities {\n  String sayHello(String name, String title);\n\n  String sayGoodBye(String name, String title);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/activities/MyActivitiesImpl.java",
    "content": "package io.temporal.samples.countinterceptor.activities;\n\npublic class MyActivitiesImpl implements MyActivities {\n  @Override\n  public String sayHello(String name, String title) {\n    return \"Hello \" + title + \" \" + name;\n  }\n\n  @Override\n  public String sayGoodBye(String name, String title) {\n    return \"Goodbye  \" + title + \" \" + name;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyChildWorkflow.java",
    "content": "package io.temporal.samples.countinterceptor.workflow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyChildWorkflow {\n  @WorkflowMethod\n  String execChild(String name, String title);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyChildWorkflowImpl.java",
    "content": "package io.temporal.samples.countinterceptor.workflow;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.countinterceptor.activities.MyActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyChildWorkflowImpl implements MyChildWorkflow {\n  @Override\n  public String execChild(String name, String title) {\n    MyActivities activities =\n        Workflow.newActivityStub(\n            MyActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    String result = activities.sayHello(name, title);\n    result += activities.sayGoodBye(name, title);\n\n    return result;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyWorkflow.java",
    "content": "package io.temporal.samples.countinterceptor.workflow;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String exec();\n\n  @SignalMethod\n  void signalNameAndTitle(String greeting, String title);\n\n  @SignalMethod\n  void exit();\n\n  @QueryMethod\n  String queryName();\n\n  @QueryMethod\n  String queryTitle();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/countinterceptor/workflow/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.countinterceptor.workflow;\n\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n\n  private String name;\n  private String title;\n  private boolean exit = false;\n\n  @Override\n  public String exec() {\n\n    // Wait for a greeting info\n    Workflow.await(() -> name != null && title != null);\n\n    // Execute child workflow\n    ChildWorkflowOptions childWorkflowOptions =\n        ChildWorkflowOptions.newBuilder().setWorkflowId(\"TestInterceptorChildWorkflow\").build();\n    MyChildWorkflow child =\n        Workflow.newChildWorkflowStub(MyChildWorkflow.class, childWorkflowOptions);\n    String result = child.execChild(name, title);\n\n    // Wait for exit signal\n    Workflow.await(Duration.ofSeconds(5), () -> exit != false);\n\n    return result;\n  }\n\n  @Override\n  public void signalNameAndTitle(String name, String title) {\n    this.name = name;\n    this.title = title;\n  }\n\n  @Override\n  public String queryName() {\n    return name;\n  }\n\n  @Override\n  public String queryTitle() {\n    return title;\n  }\n\n  @Override\n  public void exit() {\n    this.exit = true;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customannotation/BenignExceptionTypes.java",
    "content": "package io.temporal.samples.customannotation;\n\nimport java.lang.annotation.*;\n\n/**\n * BenignExceptionTypes is an annotation that can be used to specify an exception type is benign and\n * not an issue worth logging.\n *\n * <p>For this annotation to work, {@link BenignExceptionTypesAnnotationInterceptor} must be passed\n * as a worker interceptor to the worker factory.\n */\n@Documented\n@Target(ElementType.METHOD)\n@Retention(RetentionPolicy.RUNTIME)\npublic @interface BenignExceptionTypes {\n  /** Type of exceptions that should be considered benign and not logged as errors. */\n  Class<? extends Exception>[] value();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customannotation/BenignExceptionTypesAnnotationInterceptor.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.customannotation;\n\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkerInterceptorBase;\nimport io.temporal.common.metadata.POJOActivityImplMetadata;\nimport io.temporal.common.metadata.POJOActivityMethodMetadata;\nimport io.temporal.failure.ApplicationErrorCategory;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.failure.TemporalFailure;\nimport java.lang.reflect.Method;\nimport java.util.Arrays;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * Checks if the activity method has the {@link BenignExceptionTypes} annotation. If it does, it\n * will throw an ApplicationFailure with {@link ApplicationErrorCategory#BENIGN}.\n */\npublic class BenignExceptionTypesAnnotationInterceptor extends WorkerInterceptorBase {\n\n  @Override\n  public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) {\n    return new ActivityInboundCallsInterceptorAnnotation(next);\n  }\n\n  public static class ActivityInboundCallsInterceptorAnnotation\n      extends io.temporal.common.interceptors.ActivityInboundCallsInterceptorBase {\n    private final ActivityInboundCallsInterceptor next;\n    private Set<Class<? extends Exception>> benignExceptionTypes = new HashSet<>();\n\n    public ActivityInboundCallsInterceptorAnnotation(ActivityInboundCallsInterceptor next) {\n      super(next);\n      this.next = next;\n    }\n\n    @Override\n    public void init(ActivityExecutionContext context) {\n      List<POJOActivityMethodMetadata> activityMethods =\n          POJOActivityImplMetadata.newInstance(context.getInstance().getClass())\n              .getActivityMethods();\n      POJOActivityMethodMetadata currentActivityMethod =\n          activityMethods.stream()\n              .filter(x -> x.getActivityTypeName().equals(context.getInfo().getActivityType()))\n              .findFirst()\n              .get();\n      // Get the implementation method from the interface method\n      Method implementationMethod;\n      try {\n        implementationMethod =\n            context\n                .getInstance()\n                .getClass()\n                .getMethod(\n                    currentActivityMethod.getMethod().getName(),\n                    currentActivityMethod.getMethod().getParameterTypes());\n      } catch (NoSuchMethodException e) {\n        throw new RuntimeException(e);\n      }\n      // Get the @BenignExceptionTypes annotations from the implementation method\n      BenignExceptionTypes an = implementationMethod.getAnnotation(BenignExceptionTypes.class);\n      if (an != null && an.value() != null) {\n        benignExceptionTypes = new HashSet<>(Arrays.asList(an.value()));\n      }\n      next.init(context);\n    }\n\n    @Override\n    public ActivityOutput execute(ActivityInput input) {\n      if (benignExceptionTypes.isEmpty()) {\n        return next.execute(input);\n      }\n      try {\n        return next.execute(input);\n      } catch (TemporalFailure tf) {\n        throw tf;\n      } catch (Exception e) {\n        if (benignExceptionTypes.contains(e.getClass())) {\n          // If the exception is in the list of benign exceptions, throw an ApplicationFailure\n          // with a BENIGN category\n          throw ApplicationFailure.newBuilder()\n              .setMessage(e.getMessage())\n              .setType(e.getClass().getName())\n              .setCause(e)\n              .setCategory(ApplicationErrorCategory.BENIGN)\n              .build();\n        }\n        // If the exception is not in the list of benign exceptions, rethrow it\n        throw e;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customannotation/CustomAnnotation.java",
    "content": "/*\n *  Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n *\n *  Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n *\n *  Modifications copyright (C) 2017 Uber Technologies, Inc.\n *\n *  Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n *  use this file except in compliance with the License. A copy of the License is\n *  located at\n *\n *  http://aws.amazon.com/apache2.0\n *\n *  or in the \"license\" file accompanying this file. This file is distributed on\n *  an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n *  express or implied. See the License for the specific language governing\n *  permissions and limitations under the License.\n */\n\npackage io.temporal.samples.customannotation;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\npublic class CustomAnnotation {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"CustomAnnotationTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"CustomAnnotationWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see WorkflowInterface\n   * @see WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see ActivityInterface\n   * @see ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    /** Define your activity method which can be called during workflow execution */\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after activity is completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    private int callCount;\n\n    /**\n     * Our activity implementation simulates a failure 3 times. Given our previously set\n     * RetryOptions, our workflow is going to retry our activity execution.\n     */\n    @Override\n    @BenignExceptionTypes({IllegalStateException.class})\n    public synchronized String composeGreeting(String greeting, String name) {\n      if (++callCount < 4) {\n        System.out.println(\"composeGreeting activity is going to fail\");\n        throw new IllegalStateException(\"not yet\");\n      }\n\n      // after 3 unsuccessful retries we finally can complete our activity execution\n      System.out.println(\"composeGreeting activity is going to complete\");\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Get a Workflow service stub.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory =\n        WorkerFactory.newInstance(\n            client,\n            WorkerFactoryOptions.newBuilder()\n                .setWorkerInterceptors(new BenignExceptionTypesAnnotationInterceptor())\n                .build());\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Set our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     *\n     * See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customannotation/README.md",
    "content": "# Custom annotation\n\nThe sample demonstrates how to create a custom annotation using an interceptor. In this case the annotation allows specifying an exception of a certain type is benign.\n\nThis samples shows a custom annotation on an activity method, but the same approach can be used for workflow methods or Nexus operations.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.customannotation.CustomAnnotation\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionActivities.java",
    "content": "package io.temporal.samples.customchangeversion;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface CustomChangeVersionActivities {\n  String customOne(String input);\n\n  String customTwo(String input);\n\n  String customThree(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionActivitiesImpl.java",
    "content": "package io.temporal.samples.customchangeversion;\n\npublic class CustomChangeVersionActivitiesImpl implements CustomChangeVersionActivities {\n  @Override\n  public String customOne(String input) {\n    return \"\\ncustomOne activity - \" + input;\n  }\n\n  @Override\n  public String customTwo(String input) {\n    return \"\\ncustomTwo activity - \" + input;\n  }\n\n  @Override\n  public String customThree(String input) {\n    return \"\\ncustomThree activity - \" + input;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionStarter.java",
    "content": "package io.temporal.samples.customchangeversion;\n\nimport io.grpc.StatusRuntimeException;\nimport io.temporal.api.enums.v1.IndexedValueType;\nimport io.temporal.api.operatorservice.v1.AddSearchAttributesRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowServiceException;\nimport io.temporal.common.SearchAttributeKey;\nimport io.temporal.common.SearchAttributes;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.OperatorServiceStubs;\nimport io.temporal.serviceclient.OperatorServiceStubsOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.Collections;\n\npublic class CustomChangeVersionStarter {\n  private static SearchAttributeKey<String> CUSTOM_CHANGE_VERSION =\n      SearchAttributeKey.forKeyword(\"CustomChangeVersion\");\n  private static final String taskQueue = \"customChangeVersionTaskQueue\";\n  private static final String workflowId = \"CustomChangeVersionWorkflow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory workerFactory = WorkerFactory.newInstance(client);\n    Worker worker = workerFactory.newWorker(taskQueue);\n\n    // Register CustomChangeVersion search attribute thats used in this sample\n    OperatorServiceStubs operatorService =\n        OperatorServiceStubs.newServiceStubs(\n            OperatorServiceStubsOptions.newBuilder()\n                .setChannel(service.getRawChannel())\n                .validateAndBuildWithDefaults());\n    operatorService\n        .blockingStub()\n        .addSearchAttributes(\n            AddSearchAttributesRequest.newBuilder()\n                .setNamespace(client.getOptions().getNamespace())\n                .putAllSearchAttributes(\n                    Collections.singletonMap(\n                        \"CustomChangeVersion\", IndexedValueType.INDEXED_VALUE_TYPE_KEYWORD))\n                .build());\n\n    // Register workflow and activities\n    worker.registerWorkflowImplementationTypes(CustomChangeVersionWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new CustomChangeVersionActivitiesImpl());\n    workerFactory.start();\n\n    CustomChangeVersionWorkflow workflow =\n        client.newWorkflowStub(\n            CustomChangeVersionWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(taskQueue)\n                .setWorkflowId(workflowId)\n                .setTypedSearchAttributes(\n                    SearchAttributes.newBuilder().set(CUSTOM_CHANGE_VERSION, \"\").build())\n                .build());\n    try {\n      String result = workflow.run(\"Hello\");\n      System.out.println(\"Result: \" + result);\n    } catch (WorkflowServiceException e) {\n      if (e.getCause() instanceof StatusRuntimeException) {\n        StatusRuntimeException sre = (StatusRuntimeException) e.getCause();\n        System.out.println(\n            \"Error starting workflow execution: \"\n                + sre.getMessage()\n                + \" Status: \"\n                + sre.getStatus());\n      } else {\n        System.out.println(\"Error starting workflow execution: \" + e.getMessage());\n      }\n    }\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionWorkflow.java",
    "content": "package io.temporal.samples.customchangeversion;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CustomChangeVersionWorkflow {\n  @WorkflowMethod\n  String run(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/CustomChangeVersionWorkflowImpl.java",
    "content": "package io.temporal.samples.customchangeversion;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.SearchAttributeKey;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n/**\n * CustomChangeVersionWorkflowImpl shows how to upsert a custom search attribute which can be used\n * when adding changes to our workflow using workflow versioning. Note that this is only temporary\n * solution until https://github.com/temporalio/sdk-java/issues/587 is implemented. Given that a\n * number of users are in need of this and are looking for a sample, we are adding this as sample\n * until this issue is fixed, at which point it will no longer be needed.\n */\npublic class CustomChangeVersionWorkflowImpl implements CustomChangeVersionWorkflow {\n  static final SearchAttributeKey<String> CUSTOM_CHANGE_VERSION =\n      SearchAttributeKey.forKeyword(\"CustomChangeVersion\");\n  private CustomChangeVersionActivities activities =\n      Workflow.newActivityStub(\n          CustomChangeVersionActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String run(String input) {\n    String result = activities.customOne(input);\n    // We assume when this change is added we have some executions of this workflow type running\n    // where customTwo activity was not called (we are adding it to exiting workflow)\n    // Adding customTwo activity as a versioned change\n    int version = Workflow.getVersion(\"add-v2-activity-change\", Workflow.DEFAULT_VERSION, 1);\n    if (version == 1) {\n      // Upsert our custom change version search attribute\n      // We set its value to follow TemporalChangeVersion structure of \"<changeId>-<version>\"\n      Workflow.upsertTypedSearchAttributes(\n          CUSTOM_CHANGE_VERSION.valueUnset(),\n          CUSTOM_CHANGE_VERSION.valueSet(\"add-v2-activity-change-1\"));\n      // Adding call to v2 activity\n      result += activities.customTwo(input);\n    }\n\n    // lets say then later on we also want to add a change to invoke another activity\n    version = Workflow.getVersion(\"add-v3-activity-change\", Workflow.DEFAULT_VERSION, 1);\n    if (version == 1) {\n      // Upsert our custom change version search attribute\n      // We set its value to follow TemporalChangeVersion structure of \"<changeId>-<version>\"\n      Workflow.upsertTypedSearchAttributes(\n          CUSTOM_CHANGE_VERSION.valueUnset(),\n          CUSTOM_CHANGE_VERSION.valueSet(\"add-v3-activity-change-1\"));\n      // Adding call to v2 activity\n      result += activities.customThree(input);\n    }\n    return result;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/customchangeversion/README.md",
    "content": "## Custom Change Version Search Attribute Sample\n\nThis sample shows how to upsert custom search attribute when adding a version change to your workflow code.\nIt is a current workaround until feature https://github.com/temporalio/sdk-java/issues/587 is implemented.\nPurpose of upserting a custom search attribute when addint new versions is to then be able to use\nvisibility api to search for running/completed executions which are on a specific version. It is also useful to see\nif there are no running executions on specific change version in order to remove certain no longer used versioned change\nif/else block from your workflow code, so it no longer has to be maintained.\n\nTo run this sample:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.customchangeversion.CustomChangeVersionStarter\n```\n\nAfter running this sample you can go to your Web UI or use Temporal CLI to search for specific CustomChangeVersion, for example:\n\n```\ntemporal workflow list -q \"CustomChangeVersion='add-v3-activity-change-1'\"         \n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/DslActivities.java",
    "content": "package io.temporal.samples.dsl;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface DslActivities {\n  String one();\n\n  String two();\n\n  String three();\n\n  String four();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/DslActivitiesImpl.java",
    "content": "package io.temporal.samples.dsl;\n\nimport java.util.concurrent.TimeUnit;\n\npublic class DslActivitiesImpl implements DslActivities {\n  @Override\n  public String one() {\n    sleep(1);\n    return \"Activity one done...\";\n  }\n\n  @Override\n  public String two() {\n    sleep(1);\n    return \"Activity two done...\";\n  }\n\n  @Override\n  public String three() {\n    sleep(1);\n    return \"Activity three done...\";\n  }\n\n  @Override\n  public String four() {\n    sleep(1);\n    return \"Activity four done...\";\n  }\n\n  private void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException ee) {\n      // Empty\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/DslWorkflow.java",
    "content": "package io.temporal.samples.dsl;\n\nimport io.temporal.samples.dsl.model.Flow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface DslWorkflow {\n  @WorkflowMethod\n  String run(Flow flow, String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/DslWorkflowImpl.java",
    "content": "package io.temporal.samples.dsl;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.dsl.model.Flow;\nimport io.temporal.samples.dsl.model.FlowAction;\nimport io.temporal.workflow.ActivityStub;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class DslWorkflowImpl implements DslWorkflow {\n  @Override\n  public String run(Flow flow, String input) {\n    if (flow == null || flow.getActions().isEmpty()) {\n      throw ApplicationFailure.newFailure(\n          \"Flow is null or does not have any actions\", \"illegal flow\");\n    }\n\n    try {\n      return runActions(flow, input);\n    } catch (ActivityFailure e) {\n      throw ApplicationFailure.newFailure(\n          \"failing execution after compensation initiated\", e.getCause().getClass().getName());\n    }\n  }\n\n  private String runActions(Flow flow, String input) {\n    List<String> results = new ArrayList<>();\n    for (FlowAction action : flow.getActions()) {\n      // build activity options based on flow action input\n      ActivityOptions.Builder activityOptionsBuilder = ActivityOptions.newBuilder();\n      activityOptionsBuilder.setStartToCloseTimeout(\n          Duration.ofSeconds(action.getStartToCloseSec()));\n      if (action.getRetries() > 0) {\n        activityOptionsBuilder.setRetryOptions(\n            RetryOptions.newBuilder().setMaximumAttempts(action.getRetries()).build());\n      }\n      // create untyped activity stub and run activity based on flow action\n      ActivityStub activityStub = Workflow.newUntypedActivityStub(activityOptionsBuilder.build());\n\n      results.add(activityStub.execute(action.getAction(), String.class, input));\n    }\n    return String.join(\",\", results);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/README.md",
    "content": "# DSL Sample\n\nThis sample shows how to use a DSL on top of Temporal.\nThe sample defines a number of domain specific json samples \nwhich are used to define steps of actions to be performed by our workflow.\n\nAs with all samples, use the following at your own risk. \n\nAs a rule, DSLs provide limited and restrictive functionality. \nThey are not suitable for full expressive development. \n\nIn many cases, it's better to build customized DSLs to optimize simplicity and domain targeting for your particular use case.\n\n## Run the sample\n\n1Start the Starter\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.dsl.Starter\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/Starter.java",
    "content": "package io.temporal.samples.dsl;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.dsl.model.Flow;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class Starter {\n\n  public static void main(String[] args) {\n    Flow flow = getFlowFromResource();\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(\"dsl-task-queue\");\n    worker.registerWorkflowImplementationTypes(DslWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new DslActivitiesImpl());\n    factory.start();\n\n    DslWorkflow workflow =\n        client.newWorkflowStub(\n            DslWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"dsl-workflow\")\n                .setTaskQueue(\"dsl-task-queue\")\n                .build());\n\n    String result = workflow.run(flow, \"sample input\");\n\n    System.out.println(\"Result: \" + result);\n\n    System.exit(0);\n  }\n\n  private static Flow getFlowFromResource() {\n    ObjectMapper objectMapper = new ObjectMapper();\n    try {\n      return objectMapper.readValue(\n          Starter.class.getClassLoader().getResource(\"dsl/sampleflow.json\"), Flow.class);\n    } catch (Exception e) {\n      e.printStackTrace();\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/model/Flow.java",
    "content": "package io.temporal.samples.dsl.model;\n\nimport java.util.List;\n\npublic class Flow {\n  private String id;\n  private String name;\n  private String description;\n  private List<FlowAction> actions;\n\n  public Flow() {}\n\n  public Flow(String id, String name, String description, List<FlowAction> actions) {\n    this.id = id;\n    this.name = name;\n    this.description = description;\n    this.actions = actions;\n  }\n\n  public String getId() {\n    return id;\n  }\n\n  public void setId(String id) {\n    this.id = id;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public List<FlowAction> getActions() {\n    return actions;\n  }\n\n  public void setActions(List<FlowAction> actions) {\n    this.actions = actions;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/dsl/model/FlowAction.java",
    "content": "package io.temporal.samples.dsl.model;\n\npublic class FlowAction {\n  private String action;\n  private String compensateBy;\n  private int retries;\n  private int startToCloseSec;\n  private int next;\n\n  public FlowAction() {}\n\n  public FlowAction(\n      String action, String compensateBy, int retries, int startToCloseSec, int next) {\n    this.action = action;\n    this.compensateBy = compensateBy;\n    this.retries = retries;\n    this.startToCloseSec = startToCloseSec;\n    this.next = next;\n  }\n\n  public String getAction() {\n    return action;\n  }\n\n  public void setAction(String action) {\n    this.action = action;\n  }\n\n  public String getCompensateBy() {\n    return compensateBy;\n  }\n\n  public void setCompensateBy(String compensateBy) {\n    this.compensateBy = compensateBy;\n  }\n\n  public int getRetries() {\n    return retries;\n  }\n\n  public void setRetries(int retries) {\n    this.retries = retries;\n  }\n\n  public int getStartToCloseSec() {\n    return startToCloseSec;\n  }\n\n  public void setStartToCloseSec(int startToCloseSec) {\n    this.startToCloseSec = startToCloseSec;\n  }\n\n  public int getNext() {\n    return next;\n  }\n\n  public void setNext(int next) {\n    this.next = next;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturnClient.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.api.enums.v1.WorkflowIdConflictPolicy;\nimport io.temporal.client.*;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class EarlyReturnClient {\n  private static final String TASK_QUEUE = \"EarlyReturnTaskQueue\";\n  private static final String WORKFLOW_ID_PREFIX = \"early-return-workflow-\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = setupWorkflowClient();\n    runWorkflowWithUpdateWithStart(client);\n  }\n\n  // Set up the WorkflowClient\n  public static WorkflowClient setupWorkflowClient() {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    return WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n  }\n\n  // Run workflow using 'updateWithStart'\n  private static void runWorkflowWithUpdateWithStart(WorkflowClient client) {\n    TransactionRequest txRequest =\n        new TransactionRequest(\n            \"Bob\", \"Alice\",\n            1000); // Change this amount to a negative number to have initTransaction fail\n\n    WorkflowOptions options = buildWorkflowOptions();\n    TransactionWorkflow workflow = client.newWorkflowStub(TransactionWorkflow.class, options);\n\n    System.out.println(\"Starting workflow with UpdateWithStart\");\n\n    TxResult updateResult = null;\n    try {\n      updateResult =\n          WorkflowClient.executeUpdateWithStart(\n              workflow::returnInitResult,\n              UpdateOptions.<TxResult>newBuilder().build(),\n              new WithStartWorkflowOperation<>(workflow::processTransaction, txRequest));\n\n      System.out.println(\n          \"Workflow initialized with result: \"\n              + updateResult.getStatus()\n              + \" (transactionId: \"\n              + updateResult.getTransactionId()\n              + \")\");\n\n      TxResult result = WorkflowStub.fromTyped(workflow).getResult(TxResult.class);\n      System.out.println(\n          \"Workflow completed with result: \"\n              + result.getStatus()\n              + \" (transactionId: \"\n              + result.getTransactionId()\n              + \")\");\n    } catch (Exception e) {\n      System.err.println(\"Transaction initialization failed: \" + e.getMessage());\n    }\n  }\n\n  // Build WorkflowOptions with task queue and unique ID\n  private static WorkflowOptions buildWorkflowOptions() {\n    return WorkflowOptions.newBuilder()\n        .setTaskQueue(TASK_QUEUE)\n        .setWorkflowIdConflictPolicy(WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_FAIL)\n        .setWorkflowId(WORKFLOW_ID_PREFIX + System.currentTimeMillis())\n        .build();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/EarlyReturnWorker.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\n\npublic class EarlyReturnWorker {\n  private static final String TASK_QUEUE = \"EarlyReturnTaskQueue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = EarlyReturnClient.setupWorkflowClient();\n    startWorker(client);\n  }\n\n  private static void startWorker(WorkflowClient client) {\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(TransactionWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new TransactionActivitiesImpl());\n\n    factory.start();\n    System.out.println(\"Worker started\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/README.md",
    "content": "### Early-Return Sample\n\nThis sample demonstrates an early-return from a workflow.\n\nBy utilizing Update-with-Start, a client can start a new workflow and synchronously receive \na response mid-workflow, while the workflow continues to run to completion.\n\nTo run the sample, start the worker:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnWorker\n```\n\nThen, start the client:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.earlyreturn.EarlyReturnClient\n```\n\n* The client will start a workflow using Update-With-Start.\n* Update-With-Start will trigger an initialization step.\n* 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.\n* 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.\n\nTo trigger a failed initialization, set the amount to <= 0 in the `EarlyReturnClient` class's `runWorkflowWithUpdateWithStart` method and re-run the client."
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/Transaction.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\npublic final class Transaction {\n  private final String id;\n  private final String sourceAccount;\n  private final String targetAccount;\n  private final int amount;\n\n  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n  public Transaction(\n      @JsonProperty(\"id\") String id,\n      @JsonProperty(\"sourceAccount\") String sourceAccount,\n      @JsonProperty(\"targetAccount\") String targetAccount,\n      @JsonProperty(\"amount\") int amount) {\n    this.id = id;\n    this.sourceAccount = sourceAccount;\n    this.targetAccount = targetAccount;\n    this.amount = amount;\n  }\n\n  @JsonProperty(\"id\")\n  public String getId() {\n    return id;\n  }\n\n  @JsonProperty(\"sourceAccount\")\n  public String getSourceAccount() {\n    return sourceAccount;\n  }\n\n  @JsonProperty(\"targetAccount\")\n  public String getTargetAccount() {\n    return targetAccount;\n  }\n\n  @JsonProperty(\"amount\")\n  public int getAmount() {\n    return amount;\n  }\n\n  @Override\n  public String toString() {\n    return String.format(\n        \"Transaction{id='%s', sourceAccount='%s', targetAccount='%s', amount=%d}\",\n        id, sourceAccount, targetAccount, amount);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivities.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\n\n@ActivityInterface\npublic interface TransactionActivities {\n  @ActivityMethod\n  Transaction mintTransactionId(TransactionRequest txRequest);\n\n  @ActivityMethod\n  Transaction initTransaction(Transaction tx);\n\n  @ActivityMethod\n  void cancelTransaction(Transaction tx);\n\n  @ActivityMethod\n  void completeTransaction(Transaction tx);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TransactionActivitiesImpl.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.failure.ApplicationFailure;\n\npublic class TransactionActivitiesImpl implements TransactionActivities {\n\n  @Override\n  public Transaction mintTransactionId(TransactionRequest request) {\n    System.out.println(\"Minting transaction ID\");\n    // Simulate transaction ID generation\n    String txId = \"TXID\" + String.format(\"%010d\", (long) (Math.random() * 1_000_000_0000L));\n    sleep(100);\n    System.out.println(\"Transaction ID minted: \" + txId);\n    return new Transaction(\n        txId, request.getSourceAccount(), request.getTargetAccount(), request.getAmount());\n  }\n\n  @Override\n  public Transaction initTransaction(Transaction tx) {\n    System.out.println(\"Initializing transaction\");\n    sleep(300);\n    if (tx.getAmount() <= 0) {\n      System.out.println(\"Invalid amount: \" + tx.getAmount());\n      throw ApplicationFailure.newNonRetryableFailure(\n          \"Non-retryable Activity Failure: Invalid Amount\", \"InvalidAmount\");\n    }\n\n    sleep(500);\n    return tx;\n  }\n\n  @Override\n  public void cancelTransaction(Transaction tx) {\n    System.out.println(\"Cancelling transaction\");\n    sleep(300);\n    System.out.println(\"Transaction cancelled\");\n  }\n\n  @Override\n  public void completeTransaction(Transaction tx) {\n    System.out.println(\n        \"Sending $\"\n            + tx.getAmount()\n            + \" from \"\n            + tx.getSourceAccount()\n            + \" to \"\n            + tx.getTargetAccount());\n    sleep(2000);\n    System.out.println(\"Transaction completed successfully\");\n  }\n\n  private void sleep(long millis) {\n    try {\n      Thread.sleep(millis);\n    } catch (InterruptedException e) {\n      Thread.currentThread().interrupt();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TransactionRequest.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\npublic final class TransactionRequest {\n  private final String sourceAccount;\n  private final String targetAccount;\n  private final int amount;\n\n  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n  public TransactionRequest(\n      @JsonProperty(\"sourceAccount\") String sourceAccount,\n      @JsonProperty(\"targetAccount\") String targetAccount,\n      @JsonProperty(\"amount\") int amount) {\n    this.sourceAccount = sourceAccount;\n    this.targetAccount = targetAccount;\n    this.amount = amount;\n  }\n\n  @JsonProperty(\"sourceAccount\")\n  public String getSourceAccount() {\n    return sourceAccount;\n  }\n\n  @JsonProperty(\"targetAccount\")\n  public String getTargetAccount() {\n    return targetAccount;\n  }\n\n  @JsonProperty(\"amount\")\n  public int getAmount() {\n    return amount;\n  }\n\n  @Override\n  public String toString() {\n    return String.format(\n        \"TransactionRequest{sourceAccount='%s', targetAccount='%s', amount=%d}\",\n        sourceAccount, targetAccount, amount);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TransactionWorkflow.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface TransactionWorkflow {\n  @WorkflowMethod\n  TxResult processTransaction(TransactionRequest txRequest);\n\n  @UpdateMethod(name = \"early-return\")\n  TxResult returnInitResult();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TransactionWorkflowImpl.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class TransactionWorkflowImpl implements TransactionWorkflow {\n  private static final Logger log = LoggerFactory.getLogger(TransactionWorkflowImpl.class);\n  private final TransactionActivities activities =\n      Workflow.newActivityStub(\n          TransactionActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(30)).build());\n\n  private boolean initDone = false;\n  private Transaction tx;\n  private Exception initError = null;\n\n  @Override\n  public TxResult processTransaction(TransactionRequest txRequest) {\n    this.tx = activities.mintTransactionId(txRequest);\n\n    try {\n      this.tx = activities.initTransaction(this.tx);\n    } catch (Exception e) {\n      initError = e;\n    } finally {\n      initDone = true;\n    }\n\n    if (initError != null) {\n      // If initialization failed, cancel the transaction\n      activities.cancelTransaction(this.tx);\n      return new TxResult(\"\", \"Transaction cancelled.\");\n    } else {\n      activities.completeTransaction(this.tx);\n      return new TxResult(this.tx.getId(), \"Transaction completed successfully.\");\n    }\n  }\n\n  @Override\n  public TxResult returnInitResult() {\n    Workflow.await(() -> initDone);\n\n    if (initError != null) {\n      log.info(\"Initialization failed.\");\n      throw Workflow.wrap(initError);\n    }\n\n    return new TxResult(tx.getId(), \"Initialization successful\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/earlyreturn/TxResult.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\n\npublic class TxResult {\n  private final String transactionId;\n  private final String status;\n\n  // Jackson-compatible constructor with @JsonCreator and @JsonProperty annotations\n  @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n  public TxResult(\n      @JsonProperty(\"transactionId\") String transactionId, @JsonProperty(\"status\") String status) {\n    this.transactionId = transactionId;\n    this.status = status;\n  }\n\n  @JsonProperty(\"transactionId\")\n  public String getTransactionId() {\n    return transactionId;\n  }\n\n  @JsonProperty(\"status\")\n  public String getStatus() {\n    return status;\n  }\n\n  @Override\n  public String toString() {\n    return String.format(\"InitResult{transactionId='%s', status='%s'}\", transactionId, status);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/CustomerAgeCheck.java",
    "content": "package io.temporal.samples.encodefailures;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CustomerAgeCheck {\n  @WorkflowMethod\n  public String validateCustomer(MyCustomer customer);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/CustomerAgeCheckImpl.java",
    "content": "package io.temporal.samples.encodefailures;\n\nimport io.temporal.workflow.Workflow;\n\npublic class CustomerAgeCheckImpl implements CustomerAgeCheck {\n  @Override\n  public String validateCustomer(MyCustomer customer) {\n    // Note we have explicitly set InvalidCustomerException type to fail workflow execution\n    // We wrap it using Workflow.wrap so can throw as unchecked\n    if (customer.getAge() < 21) {\n      throw Workflow.wrap(\n          new InvalidCustomerException(\"customer \" + customer.getName() + \" is under age.\"));\n    } else {\n      return \"done...\";\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/InvalidCustomerException.java",
    "content": "package io.temporal.samples.encodefailures;\n\npublic class InvalidCustomerException extends Exception {\n  public InvalidCustomerException(String errorMessage) {\n    super(errorMessage);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/MyCustomer.java",
    "content": "package io.temporal.samples.encodefailures;\n\npublic class MyCustomer {\n  private String name;\n  private int age;\n  private boolean approved;\n\n  public MyCustomer() {}\n\n  public MyCustomer(String name, int age) {\n    this.name = name;\n    this.age = age;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public int getAge() {\n    return age;\n  }\n\n  public void setAge(int age) {\n    this.age = age;\n  }\n\n  public boolean isApproved() {\n    return approved;\n  }\n\n  public void setApproved(boolean approved) {\n    this.approved = approved;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/README.md",
    "content": "# Using Codec to encode / decode failure messages\n\nThe sample demonstrates how to set up a simple codec for encoding/decoding failure messages\nIn this sample we set encodeFailureAttributes = true to our CodecDataConverter meaning we want to \nencode / decode failure messages as well.\nAll it does is add a \"Customer: \" prefix to the message. You can expand on this to add any type of \nencoding that you might want to use.\n\nOur workflow does simple customer age check validation and fails if their age is < 21.\nIn the Starter then we print out that the failure message client received on execution failure\nwas indeed encoded using our codec.\n\n## Running\n\n1. Start Temporal Server with \"default\" namespace enabled.\n   For example using local Docker:\n\n```bash\ngit clone https://github.com/temporalio/docker-compose.git\ncd  docker-compose\ndocker-compose up\n```\n\n2. Run the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.encodefailures.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/SimplePrefixPayloadCodec.java",
    "content": "package io.temporal.samples.encodefailures;\n\nimport com.google.protobuf.ByteString;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.payload.codec.PayloadCodec;\nimport io.temporal.payload.codec.PayloadCodecException;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.jetbrains.annotations.NotNull;\n\n/**\n * Simple codec that adds dummy prefix to payload. For this sample it's also applied for failure\n * messages.\n */\npublic class SimplePrefixPayloadCodec implements PayloadCodec {\n\n  public static final ByteString PREFIX = ByteString.copyFromUtf8(\"Customer: \");\n\n  @NotNull\n  @Override\n  public List<Payload> encode(@NotNull List<Payload> payloads) {\n    return payloads.stream().map(this::encode).collect(Collectors.toList());\n  }\n\n  private Payload encode(Payload decodedPayload) {\n    ByteString encodedData = PREFIX.concat(decodedPayload.getData());\n    return decodedPayload.toBuilder().setData(encodedData).build();\n  }\n\n  @NotNull\n  @Override\n  public List<Payload> decode(@NotNull List<Payload> payloads) {\n    return payloads.stream().map(this::decode).collect(Collectors.toList());\n  }\n\n  private Payload decode(Payload encodedPayload) {\n    ByteString encodedData = encodedPayload.getData();\n    if (!encodedData.startsWith(PREFIX))\n      throw new PayloadCodecException(\"Payload is not correctly encoded\");\n    ByteString decodedData = encodedData.substring(PREFIX.size());\n    return encodedPayload.toBuilder().setData(decodedData).build();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encodefailures/Starter.java",
    "content": "package io.temporal.samples.encodefailures;\n\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.api.history.v1.HistoryEvent;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.CodecDataConverter;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport java.io.IOException;\nimport java.util.Collections;\n\npublic class Starter {\n  private static final String TASK_QUEUE = \"EncodeDecodeFailuresTaskQueue\";\n  private static final String WORKFLOW_ID = \"CustomerValidationWorkflow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    // CodecDataConverter defines our data converter and codec\n    // sets encodeFailureAttributes to true\n    CodecDataConverter codecDataConverter =\n        new CodecDataConverter(\n            // For sample we just use default data converter\n            DefaultDataConverter.newDefaultInstance(),\n            // Simple prefix codec to encode/decode\n            Collections.singletonList(new SimplePrefixPayloadCodec()),\n            true); // Setting encodeFailureAttributes to true\n\n    // WorkflowClient uses our CodecDataConverter\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder().setDataConverter(codecDataConverter).build());\n\n    // Create worker and start Worker factory\n    createWorker(client);\n\n    // Start workflow execution and catch client error (workflow execution fails)\n    CustomerAgeCheck workflow =\n        client.newWorkflowStub(\n            CustomerAgeCheck.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    try {\n      // Start workflow execution to validate under-age customer\n      workflow.validateCustomer(new MyCustomer(\"John\", 17));\n      System.out.println(\"Workflow should have failed on customer validation\");\n    } catch (WorkflowFailedException e) {\n      // Get failure message from last event in history (WorkflowExecutionFailed event) and check\n      // that\n      // its encoded\n      HistoryEvent wfExecFailedEvent = client.fetchHistory(WORKFLOW_ID).getLastEvent();\n      Payload payload =\n          wfExecFailedEvent\n              .getWorkflowExecutionFailedEventAttributes()\n              .getFailure()\n              .getEncodedAttributes();\n      if (isEncoded(payload)) {\n        System.out.println(\"Workflow failure was encoded\");\n      } else {\n        System.out.println(\"Workflow failure was not encoded\");\n      }\n    }\n\n    // Stop sample\n    System.exit(0);\n  }\n\n  private static boolean isEncoded(Payload payload) {\n    return payload.getData().startsWith(SimplePrefixPayloadCodec.PREFIX);\n  }\n\n  private static void createWorker(WorkflowClient client) {\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            // note we set InvalidCustomerException to fail execution\n            .setFailWorkflowExceptionTypes(InvalidCustomerException.class)\n            .build(),\n        CustomerAgeCheckImpl.class);\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encryptedpayloads/CryptCodec.java",
    "content": "package io.temporal.samples.encryptedpayloads;\n\nimport com.google.protobuf.ByteString;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.common.converter.DataConverterException;\nimport io.temporal.common.converter.EncodingKeys;\nimport io.temporal.payload.codec.PayloadCodec;\nimport io.temporal.payload.codec.PayloadCodecException;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.security.SecureRandom;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport org.jetbrains.annotations.NotNull;\n\nclass CryptCodec implements PayloadCodec {\n  static final ByteString METADATA_ENCODING =\n      ByteString.copyFrom(\"binary/encrypted\", StandardCharsets.UTF_8);\n\n  private static final String CIPHER = \"AES/GCM/NoPadding\";\n\n  static final String METADATA_ENCRYPTION_CIPHER_KEY = \"encryption-cipher\";\n  static final ByteString METADATA_ENCRYPTION_CIPHER =\n      ByteString.copyFrom(CIPHER, StandardCharsets.UTF_8);\n\n  static final String METADATA_ENCRYPTION_KEY_ID_KEY = \"encryption-key-id\";\n\n  private static final int GCM_NONCE_LENGTH_BYTE = 12;\n  private static final int GCM_TAG_LENGTH_BIT = 128;\n  private static final Charset UTF_8 = StandardCharsets.UTF_8;\n\n  @NotNull\n  @Override\n  public List<Payload> encode(@NotNull List<Payload> payloads) {\n    return payloads.stream().map(this::encodePayload).collect(Collectors.toList());\n  }\n\n  @NotNull\n  @Override\n  public List<Payload> decode(@NotNull List<Payload> payloads) {\n    return payloads.stream().map(this::decodePayload).collect(Collectors.toList());\n  }\n\n  private Payload encodePayload(Payload payload) {\n    String keyId = getKeyId();\n    SecretKey key = getKey(keyId);\n\n    byte[] encryptedData;\n    try {\n      encryptedData = encrypt(payload.toByteArray(), key);\n    } catch (Throwable e) {\n      throw new DataConverterException(e);\n    }\n\n    return Payload.newBuilder()\n        .putMetadata(EncodingKeys.METADATA_ENCODING_KEY, METADATA_ENCODING)\n        .putMetadata(METADATA_ENCRYPTION_CIPHER_KEY, METADATA_ENCRYPTION_CIPHER)\n        .putMetadata(METADATA_ENCRYPTION_KEY_ID_KEY, ByteString.copyFromUtf8(keyId))\n        .setData(ByteString.copyFrom(encryptedData))\n        .build();\n  }\n\n  private Payload decodePayload(Payload payload) {\n    if (METADATA_ENCODING.equals(\n        payload.getMetadataOrDefault(EncodingKeys.METADATA_ENCODING_KEY, null))) {\n      String keyId;\n      try {\n        keyId = payload.getMetadataOrThrow(METADATA_ENCRYPTION_KEY_ID_KEY).toString(UTF_8);\n      } catch (Exception e) {\n        throw new PayloadCodecException(e);\n      }\n      SecretKey key = getKey(keyId);\n\n      byte[] plainData;\n      Payload decryptedPayload;\n\n      try {\n        plainData = decrypt(payload.getData().toByteArray(), key);\n        decryptedPayload = Payload.parseFrom(plainData);\n        return decryptedPayload;\n      } catch (Throwable e) {\n        throw new PayloadCodecException(e);\n      }\n    } else {\n      return payload;\n    }\n  }\n\n  private String getKeyId() {\n    // Currently there is no context available to vary which key is used.\n    // Use a fixed key for all payloads.\n    // This still supports key rotation as the key ID is recorded on payloads allowing\n    // decryption to use a previous key.\n\n    return \"test-key-test-key-test-key-test!\";\n  }\n\n  private SecretKey getKey(String keyId) {\n    // Key must be fetched from KMS or other secure storage.\n    // Hard coded here only for example purposes.\n    return new SecretKeySpec(keyId.getBytes(UTF_8), \"AES\");\n  }\n\n  private static byte[] getNonce(int size) {\n    byte[] nonce = new byte[size];\n    new SecureRandom().nextBytes(nonce);\n    return nonce;\n  }\n\n  private byte[] encrypt(byte[] plainData, SecretKey key) throws Exception {\n    byte[] nonce = getNonce(GCM_NONCE_LENGTH_BYTE);\n\n    Cipher cipher = Cipher.getInstance(CIPHER);\n    cipher.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH_BIT, nonce));\n\n    byte[] encryptedData = cipher.doFinal(plainData);\n    return ByteBuffer.allocate(nonce.length + encryptedData.length)\n        .put(nonce)\n        .put(encryptedData)\n        .array();\n  }\n\n  private byte[] decrypt(byte[] encryptedDataWithNonce, SecretKey key) throws Exception {\n    ByteBuffer buffer = ByteBuffer.wrap(encryptedDataWithNonce);\n\n    byte[] nonce = new byte[GCM_NONCE_LENGTH_BYTE];\n    buffer.get(nonce);\n    byte[] encryptedData = new byte[buffer.remaining()];\n    buffer.get(encryptedData);\n\n    Cipher cipher = Cipher.getInstance(CIPHER);\n    cipher.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(GCM_TAG_LENGTH_BIT, nonce));\n\n    return cipher.doFinal(encryptedData);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/encryptedpayloads/EncryptedPayloadsActivity.java",
    "content": "package io.temporal.samples.encryptedpayloads;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.CodecDataConverter;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.Collections;\n\n/**\n * Hello World Temporal workflow that executes a single activity. Requires a local instance the\n * Temporal service to be running.\n */\npublic class EncryptedPayloadsActivity {\n\n  static final String TASK_QUEUE = \"EncryptedPayloads\";\n\n  /** Workflow interface has to have at least one method annotated with @WorkflowMethod. */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /** Activity interface is just a POJI. */\n  @ActivityInterface\n  public interface GreetingActivities {\n    @ActivityMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  /** GreetingWorkflow implementation that calls GreetingsActivities#composeGreeting. */\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Activity stub implements activity interface and proxies calls to it to Temporal activity\n     * invocations. Because activities are reentrant, only a single stub can be used for multiple\n     * activity invocations.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  public static void main(String[] args) {\n    // gRPC stubs wrapper that talks to the local docker instance of temporal service.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder()\n                .setDataConverter(\n                    new CodecDataConverter(\n                        DefaultDataConverter.newDefaultInstance(),\n                        Collections.singletonList(new CryptCodec())))\n                .build());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    // Workflows are stateful. So you need a type to create instances.\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n    // Activities are stateless and thread safe. So a shared instance is used.\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n    // Start listening to the workflow and activity task queues.\n    factory.start();\n\n    // Start a workflow execution. Usually this is done from another program.\n    // Uses task queue from the GreetingWorkflow @WorkflowMethod annotation.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build());\n    // Execute a workflow waiting for it to complete. See {@link\n    // io.temporal.samples.hello.HelloSignal}\n    // for an example of starting workflow without waiting synchronously for its result.\n    String greeting = workflow.getGreeting(\"My Secret Friend\");\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/envconfig/LoadFromFile.java",
    "content": "package io.temporal.samples.envconfig;\n\n// @@@SNIPSTART java-env-config-profile\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport java.nio.file.Paths;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This sample demonstrates loading the default environment configuration profile from a TOML file.\n */\npublic class LoadFromFile {\n\n  private static final Logger logger = LoggerFactory.getLogger(LoadFromFile.class);\n\n  public static void main(String[] args) {\n    try {\n      // For this sample to be self-contained, we explicitly provide the path to\n      // the config.toml file included in this directory.\n      // By default though, the config.toml file will be loaded from\n      // ~/.config/temporal/temporal.toml (or the equivalent standard config directory on your OS).\n      String configFilePath =\n          Paths.get(LoadFromFile.class.getResource(\"/config.toml\").toURI()).toString();\n\n      logger.info(\"--- Loading 'default' profile from {} ---\", configFilePath);\n\n      // Load client profile from file. By default, this loads the \"default\" profile\n      // and applies any environment variable overrides.\n      ClientConfigProfile profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .build());\n\n      // Convert profile to client options (equivalent to Python's load_client_connect_config)\n      WorkflowServiceStubsOptions serviceStubsOptions = profile.toWorkflowServiceStubsOptions();\n      WorkflowClientOptions clientOptions = profile.toWorkflowClientOptions();\n\n      logger.info(\"Loaded 'default' profile from {}\", configFilePath);\n      logger.info(\"  Address: {}\", serviceStubsOptions.getTarget());\n      logger.info(\"  Namespace: {}\", clientOptions.getNamespace());\n      if (serviceStubsOptions.getHeaders() != null\n          && !serviceStubsOptions.getHeaders().keys().isEmpty()) {\n        logger.info(\"  gRPC Metadata keys: {}\", serviceStubsOptions.getHeaders().keys());\n      }\n\n      logger.info(\"\\nAttempting to connect to client...\");\n\n      try {\n        // Create the workflow client using the loaded configuration\n        WorkflowClient client =\n            WorkflowClient.newInstance(\n                WorkflowServiceStubs.newServiceStubs(serviceStubsOptions), clientOptions);\n\n        // Test the connection by getting system info\n        var systemInfo =\n            client\n                .getWorkflowServiceStubs()\n                .blockingStub()\n                .getSystemInfo(\n                    io.temporal.api.workflowservice.v1.GetSystemInfoRequest.getDefaultInstance());\n\n        logger.info(\"✅ Client connected successfully!\");\n        logger.info(\"   Server version: {}\", systemInfo.getServerVersion());\n\n      } catch (Exception e) {\n        logger.error(\"❌ Failed to connect: {}\", e.getMessage());\n      }\n\n    } catch (Exception e) {\n      logger.error(\"Failed to load configuration: {}\", e.getMessage(), e);\n      System.exit(1);\n    }\n  }\n}\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/envconfig/LoadProfile.java",
    "content": "package io.temporal.samples.envconfig;\n\n// @@@SNIPSTART java-env-config-profile-with-overrides\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport java.nio.file.Paths;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * This sample demonstrates loading a specific profile from a TOML configuration file with\n * programmatic overrides.\n */\npublic class LoadProfile {\n\n  private static final Logger logger = LoggerFactory.getLogger(LoadProfile.class);\n\n  public static void main(String[] args) {\n    String profileName = \"staging\";\n\n    try {\n      // For this sample to be self-contained, we explicitly provide the path to\n      // the config.toml file included in this directory.\n      String configFilePath =\n          Paths.get(LoadProfile.class.getResource(\"/config.toml\").toURI()).toString();\n\n      logger.info(\"--- Loading '{}' profile from {} ---\", profileName, configFilePath);\n\n      // Load specific profile from file with environment variable overrides\n      ClientConfigProfile profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(profileName)\n                  .build());\n\n      // Demonstrate programmatic override - fix the incorrect address from staging profile\n      logger.info(\"\\n--- Applying programmatic override ---\");\n      ClientConfigProfile.Builder profileBuilder = profile.toBuilder();\n      profileBuilder.setAddress(\"localhost:7233\"); // Override the incorrect address\n      profile = profileBuilder.build();\n      logger.info(\"  Overridden address to: {}\", profile.getAddress());\n\n      // Convert profile to client options (equivalent to Python's load_client_connect_config)\n      WorkflowServiceStubsOptions serviceStubsOptions = profile.toWorkflowServiceStubsOptions();\n      WorkflowClientOptions clientOptions = profile.toWorkflowClientOptions();\n\n      logger.info(\"Loaded '{}' profile from {}\", profileName, configFilePath);\n      logger.info(\"  Address: {}\", serviceStubsOptions.getTarget());\n      logger.info(\"  Namespace: {}\", clientOptions.getNamespace());\n      if (serviceStubsOptions.getHeaders() != null\n          && !serviceStubsOptions.getHeaders().keys().isEmpty()) {\n        logger.info(\"  gRPC Metadata keys: {}\", serviceStubsOptions.getHeaders().keys());\n      }\n\n      logger.info(\"\\nAttempting to connect to client...\");\n\n      try {\n        // Create the workflow client using the loaded configuration\n        WorkflowClient client =\n            WorkflowClient.newInstance(\n                WorkflowServiceStubs.newServiceStubs(serviceStubsOptions), clientOptions);\n\n        // Test the connection by getting system info\n        var systemInfo =\n            client\n                .getWorkflowServiceStubs()\n                .blockingStub()\n                .getSystemInfo(\n                    io.temporal.api.workflowservice.v1.GetSystemInfoRequest.getDefaultInstance());\n\n        logger.info(\"✅ Client connected successfully!\");\n        logger.info(\"   Server version: {}\", systemInfo.getServerVersion());\n\n      } catch (Exception e) {\n        logger.error(\"❌ Failed to connect: {}\", e.getMessage());\n      }\n\n    } catch (Exception e) {\n      logger.error(\"Failed to load configuration: {}\", e.getMessage(), e);\n      System.exit(1);\n    }\n  }\n}\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/envconfig/README.md",
    "content": "# Environment Configuration Sample\n\nThis sample demonstrates how to configure a Temporal client using TOML configuration files. This allows you to manage connection settings across different environments without hardcoding them.\n\nThe `config.toml` file defines three profiles:\n- `[profile.default]`: Local development configuration\n- `[profile.staging]`: Configuration with incorrect address to demonstrate overrides\n- `[profile.prod]`: Example production configuration (not runnable)\n\n**Load from file (default profile):**\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.envconfig.LoadFromFile\n```\n\n**Load specific profile with overrides:**\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.envconfig.LoadProfile\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/README.md",
    "content": "# Excluding certain Workflow and Activity Types from interceptors\n\nThis sample shows how to exclude certain workflow types and Activity types from Workflow and Activity Interceptors.\n\n1. Start the Sample:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.excludefrominterceptor.RunMyWorkflows\n```\n\nObserve the event histories of MyWorkflowOne and MyWorkflowTwo in your Temporal Web UI. \nYou should see that even tho both executions were served by same worker so both had the interceptors applied,\nMyWorkflowTwo was excluded from being applied by these interceptors.\n\nAlso from the Activity interceptor logs (System.out prints during sample run) note that \nonly ActivityOne activity is being intercepted and not ActivityTwo or the \"ForInterceptor\" activities.\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/RunMyWorkflows.java",
    "content": "package io.temporal.samples.excludefrominterceptor;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.excludefrominterceptor.activities.ForInterceptorActivitiesImpl;\nimport io.temporal.samples.excludefrominterceptor.activities.MyActivitiesImpl;\nimport io.temporal.samples.excludefrominterceptor.interceptor.MyWorkerInterceptor;\nimport io.temporal.samples.excludefrominterceptor.workflows.*;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.io.IOException;\nimport java.util.Arrays;\nimport java.util.concurrent.CompletableFuture;\n\npublic class RunMyWorkflows {\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactoryOptions wfo =\n        WorkerFactoryOptions.newBuilder()\n            // exclude MyWorkflowTwo from interceptor\n            .setWorkerInterceptors(\n                new MyWorkerInterceptor(\n                    // exclude MyWorkflowTwo from workflow interceptors\n                    Arrays.asList(MyWorkflowTwo.class.getSimpleName()),\n                    // exclude ActivityTwo and the \"ForInterceptor\" activities from activity\n                    // interceptor\n                    // note with SpringBoot starter you could use bean names here, we use strings to\n                    // not have\n                    // to reflect on the activity impl class in sample\n                    Arrays.asList(\n                        \"ActivityTwo\", \"ForInterceptorActivityOne\", \"ForInterceptorActivityTwo\")))\n            .validateAndBuildWithDefaults();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client, wfo);\n    Worker worker = factory.newWorker(\"exclude-from-interceptor-queue\");\n    worker.registerWorkflowImplementationTypes(MyWorkflowOneImpl.class, MyWorkflowTwoImpl.class);\n    worker.registerActivitiesImplementations(\n        new MyActivitiesImpl(), new ForInterceptorActivitiesImpl());\n\n    factory.start();\n\n    MyWorkflow myWorkflow =\n        client.newWorkflowStub(\n            MyWorkflowOne.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"MyWorkflowOne\")\n                .setTaskQueue(\"exclude-from-interceptor-queue\")\n                .build());\n\n    MyWorkflowTwo myWorkflowTwo =\n        client.newWorkflowStub(\n            MyWorkflowTwo.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"MyWorkflowTwo\")\n                .setTaskQueue(\"exclude-from-interceptor-queue\")\n                .build());\n\n    WorkflowClient.start(myWorkflow::execute, \"my workflow input\");\n    WorkflowClient.start(myWorkflowTwo::execute, \"my workflow two input\");\n\n    // wait for both execs to complete\n    try {\n      CompletableFuture.allOf(\n              WorkflowStub.fromTyped(myWorkflow).getResultAsync(String.class),\n              WorkflowStub.fromTyped(myWorkflowTwo).getResultAsync(String.class))\n          .get();\n    } catch (Exception e) {\n      System.out.println(\"Error: \" + e.getMessage());\n    }\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/ForInterceptorActivities.java",
    "content": "package io.temporal.samples.excludefrominterceptor.activities;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface ForInterceptorActivities {\n  void forInterceptorActivityOne(Object output);\n\n  void forInterceptorActivityTwo(Object output);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/ForInterceptorActivitiesImpl.java",
    "content": "package io.temporal.samples.excludefrominterceptor.activities;\n\npublic class ForInterceptorActivitiesImpl implements ForInterceptorActivities {\n  @Override\n  public void forInterceptorActivityOne(Object output) {}\n\n  @Override\n  public void forInterceptorActivityTwo(Object output) {}\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/MyActivities.java",
    "content": "package io.temporal.samples.excludefrominterceptor.activities;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface MyActivities {\n  void activityOne(String input);\n\n  void activityTwo(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/activities/MyActivitiesImpl.java",
    "content": "package io.temporal.samples.excludefrominterceptor.activities;\n\npublic class MyActivitiesImpl implements MyActivities {\n  @Override\n  public void activityOne(String input) {}\n\n  @Override\n  public void activityTwo(String input) {}\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/interceptor/MyActivityInboundCallsInterceptor.java",
    "content": "package io.temporal.samples.excludefrominterceptor.interceptor;\n\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptor;\nimport io.temporal.common.interceptors.ActivityInboundCallsInterceptorBase;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MyActivityInboundCallsInterceptor extends ActivityInboundCallsInterceptorBase {\n\n  private ActivityExecutionContext activityExecutionContext;\n  private List<String> excludeActivityTypes = new ArrayList<>();\n\n  public MyActivityInboundCallsInterceptor(ActivityInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  public MyActivityInboundCallsInterceptor(\n      List<String> excludeActivityTypes, ActivityInboundCallsInterceptor next) {\n    super(next);\n    this.excludeActivityTypes = excludeActivityTypes;\n  }\n\n  @Override\n  public void init(ActivityExecutionContext context) {\n    this.activityExecutionContext = context;\n    super.init(context);\n  }\n\n  @Override\n  public ActivityOutput execute(ActivityInput input) {\n    if (!excludeActivityTypes.contains(activityExecutionContext.getInfo().getActivityType())) {\n      // If activity retry attempt is > X then we want to log this (or push to metrics or similar)\n      // for demo we just use >=1 just to log and dont have to explicitly fail our sample activities\n      if (activityExecutionContext.getInfo().getAttempt() >= 1) {\n        System.out.println(\n            \"Activity retry attempt noted - \"\n                + activityExecutionContext.getInfo().getWorkflowType()\n                + \" - \"\n                + activityExecutionContext.getInfo().getActivityType());\n      }\n    }\n    return super.execute(input);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/interceptor/MyWorkerInterceptor.java",
    "content": "package io.temporal.samples.excludefrominterceptor.interceptor;\n\nimport io.temporal.common.interceptors.*;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MyWorkerInterceptor extends WorkerInterceptorBase {\n  private List<String> excludeWorkflowTypes = new ArrayList<>();\n  private List<String> excludeActivityTypes = new ArrayList<>();\n\n  public MyWorkerInterceptor() {}\n\n  public MyWorkerInterceptor(List<String> excludeWorkflowTypes) {\n    this.excludeWorkflowTypes = excludeWorkflowTypes;\n  }\n\n  public MyWorkerInterceptor(List<String> excludeWorkflowTypes, List<String> excludeActivityTypes) {\n    this.excludeWorkflowTypes = excludeWorkflowTypes;\n    this.excludeActivityTypes = excludeActivityTypes;\n  }\n\n  @Override\n  public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) {\n    return new MyWorkflowInboundCallsInterceptor(excludeWorkflowTypes, next);\n  }\n\n  @Override\n  public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) {\n    return new MyActivityInboundCallsInterceptor(excludeActivityTypes, next);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/interceptor/MyWorkflowInboundCallsInterceptor.java",
    "content": "package io.temporal.samples.excludefrominterceptor.interceptor;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptorBase;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport io.temporal.samples.excludefrominterceptor.activities.ForInterceptorActivities;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInfo;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MyWorkflowInboundCallsInterceptor extends WorkflowInboundCallsInterceptorBase {\n  private WorkflowInfo workflowInfo;\n  private List<String> excludeWorkflowTypes = new ArrayList<>();\n  private ForInterceptorActivities activities =\n      Workflow.newActivityStub(\n          ForInterceptorActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  public MyWorkflowInboundCallsInterceptor(WorkflowInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  public MyWorkflowInboundCallsInterceptor(\n      List<String> excludeWorkflowTypes, WorkflowInboundCallsInterceptor next) {\n    super(next);\n    this.excludeWorkflowTypes = excludeWorkflowTypes;\n  }\n\n  @Override\n  public void init(WorkflowOutboundCallsInterceptor outboundCalls) {\n    this.workflowInfo = Workflow.getInfo();\n    super.init(new MyWorkflowOutboundCallsInterceptor(excludeWorkflowTypes, outboundCalls));\n  }\n\n  @Override\n  public WorkflowOutput execute(WorkflowInput input) {\n    WorkflowOutput output = super.execute(input);\n    if (!excludeWorkflowTypes.contains(workflowInfo.getWorkflowType())) {\n      // After workflow completes we want to execute activity to lets say persist its result to db\n      // or similar\n      activities.forInterceptorActivityOne(output.getResult());\n    }\n    return output;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/interceptor/MyWorkflowOutboundCallsInterceptor.java",
    "content": "package io.temporal.samples.excludefrominterceptor.interceptor;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptorBase;\nimport io.temporal.samples.excludefrominterceptor.activities.ForInterceptorActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class MyWorkflowOutboundCallsInterceptor extends WorkflowOutboundCallsInterceptorBase {\n  private List<String> excludeWorkflowTypes = new ArrayList<>();\n  private ForInterceptorActivities activities =\n      Workflow.newActivityStub(\n          ForInterceptorActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  public MyWorkflowOutboundCallsInterceptor(WorkflowOutboundCallsInterceptor next) {\n    super(next);\n  }\n\n  public MyWorkflowOutboundCallsInterceptor(\n      List<String> excludeWorkflowTypes, WorkflowOutboundCallsInterceptor next) {\n    super(next);\n    this.excludeWorkflowTypes = excludeWorkflowTypes;\n  }\n\n  @Override\n  public <R> ActivityOutput<R> executeActivity(ActivityInput<R> input) {\n    ActivityOutput output = super.executeActivity(input);\n    if (!excludeWorkflowTypes.contains(Workflow.getInfo().getWorkflowType())) {\n      // After activity completes we want to execute activity to lets say persist its result to db\n      // or similar\n      activities.forInterceptorActivityTwo(output.getResult().get());\n    }\n    return output;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflow.java",
    "content": "package io.temporal.samples.excludefrominterceptor.workflows;\n\nimport io.temporal.workflow.WorkflowMethod;\n\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String execute(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowOne.java",
    "content": "package io.temporal.samples.excludefrominterceptor.workflows;\n\nimport io.temporal.workflow.WorkflowInterface;\n\n@WorkflowInterface\npublic interface MyWorkflowOne extends MyWorkflow {}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowOneImpl.java",
    "content": "package io.temporal.samples.excludefrominterceptor.workflows;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.excludefrominterceptor.activities.MyActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowOneImpl implements MyWorkflowOne {\n  private MyActivities activities =\n      Workflow.newActivityStub(\n          MyActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String execute(String input) {\n    activities.activityOne(input);\n    activities.activityTwo(input);\n\n    return \"done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowTwo.java",
    "content": "package io.temporal.samples.excludefrominterceptor.workflows;\n\nimport io.temporal.workflow.WorkflowInterface;\n\n@WorkflowInterface\npublic interface MyWorkflowTwo extends MyWorkflow {}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/excludefrominterceptor/workflows/MyWorkflowTwoImpl.java",
    "content": "package io.temporal.samples.excludefrominterceptor.workflows;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.excludefrominterceptor.activities.MyActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowTwoImpl implements MyWorkflowTwo {\n  private MyActivities activities =\n      Workflow.newActivityStub(\n          MyActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String execute(String input) {\n    activities.activityOne(input);\n    activities.activityTwo(input);\n\n    return \"done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/FileProcessingStarter.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport static io.temporal.samples.fileprocessing.FileProcessingWorker.TASK_QUEUE;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.net.URL;\n\n/** Starts a file processing sample workflow. */\npublic class FileProcessingStarter {\n\n  public static void main(String[] args) throws Exception {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    FileProcessingWorkflow workflow =\n        client.newWorkflowStub(\n            FileProcessingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build());\n\n    System.out.println(\"Executing FileProcessingWorkflow\");\n\n    URL source = new URL(\"http://www.google.com/\");\n    URL destination = new URL(\"http://dummy\");\n\n    // This is going to block until the workflow completes.\n    // This is rarely used in production. Use the commented code below for async start version.\n    workflow.processFile(source, destination);\n    System.out.println(\"FileProcessingWorkflow completed\");\n\n    // Use this code instead of the above blocking call to start workflow asynchronously.\n    //    WorkflowExecution workflowExecution =\n    //        WorkflowClient.start(workflow::processFile, source, destination);\n    //    System.out.println(\n    //        \"Started periodic workflow with workflowId=\\\"\"\n    //            + workflowExecution.getWorkflowId()\n    //            + \"\\\" and runId=\\\"\"\n    //            + workflowExecution.getRunId()\n    //            + \"\\\"\");\n    //\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/FileProcessingWorker.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.lang.management.ManagementFactory;\n\n/**\n * This is the process that hosts all workflows and activities in this sample. Run multiple\n * instances of the worker in different windows. Then start a workflow by running the\n * FileProcessingStarter. Note that all activities always execute on the same worker. But each time\n * they might end up on a different worker as the first activity is dispatched to the common task\n * list.\n */\npublic class FileProcessingWorker {\n\n  static final String TASK_QUEUE = \"FileProcessing\";\n\n  public static void main(String[] args) {\n\n    String hostSpecifiTaskQueue = ManagementFactory.getRuntimeMXBean().getName();\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    final Worker workerForCommonTaskQueue = factory.newWorker(TASK_QUEUE);\n    workerForCommonTaskQueue.registerWorkflowImplementationTypes(FileProcessingWorkflowImpl.class);\n    StoreActivitiesImpl storeActivityImpl = new StoreActivitiesImpl(hostSpecifiTaskQueue);\n    workerForCommonTaskQueue.registerActivitiesImplementations(storeActivityImpl);\n\n    // Get worker to poll the host-specific task queue.\n    final Worker workerForHostSpecificTaskQueue = factory.newWorker(hostSpecifiTaskQueue);\n    workerForHostSpecificTaskQueue.registerActivitiesImplementations(storeActivityImpl);\n\n    // Start all workers created by this factory.\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n    System.out.println(\"Worker Started for activity task Queue: \" + hostSpecifiTaskQueue);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/FileProcessingWorkflow.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.net.URL;\n\n/** Contract for file processing workflow. */\n@WorkflowInterface\npublic interface FileProcessingWorkflow {\n  @WorkflowMethod\n  void processFile(URL source, URL destination);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/FileProcessingWorkflowImpl.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.workflow.Workflow;\nimport java.net.URL;\nimport java.time.Duration;\nimport java.util.Optional;\n\n/**\n * This implementation of FileProcessingWorkflow downloads the file, zips it, and uploads it to a\n * destination. An important requirement for such a workflow is that while a first activity can run\n * on any host, the second and third must run on the same host as the first one. This is achieved\n * through use of a host specific task queue. The first activity returns the name of the host\n * specific task queue and all other activities are dispatched using the stub that is configured\n * with it. This assumes that FileProcessingWorker has a worker running on the same task queue.\n */\npublic class FileProcessingWorkflowImpl implements FileProcessingWorkflow {\n\n  // Uses the default task queue shared by the pool of workers.\n  private final StoreActivities defaultTaskQueueActivities;\n\n  public FileProcessingWorkflowImpl() {\n    // Create activity clients.\n    ActivityOptions ao =\n        ActivityOptions.newBuilder()\n            .setStartToCloseTimeout(Duration.ofSeconds(20))\n            .setRetryOptions(\n                RetryOptions.newBuilder()\n                    .setInitialInterval(Duration.ofSeconds(1))\n                    .setMaximumAttempts(4)\n                    .setDoNotRetry(IllegalArgumentException.class.getName())\n                    .build())\n            .build();\n    this.defaultTaskQueueActivities = Workflow.newActivityStub(StoreActivities.class, ao);\n  }\n\n  @Override\n  public void processFile(URL source, URL destination) {\n    RetryOptions retryOptions =\n        RetryOptions.newBuilder().setInitialInterval(Duration.ofSeconds(1)).build();\n    // Retries the whole sequence on any failure, potentially on a different host.\n    Workflow.retry(retryOptions, Optional.empty(), () -> processFileImpl(source, destination));\n  }\n\n  private void processFileImpl(URL source, URL destination) {\n    StoreActivities.TaskQueueFileNamePair downloaded = defaultTaskQueueActivities.download(source);\n\n    // Now initialize stubs that are specific to the returned task queue.\n    ActivityOptions hostActivityOptions =\n        ActivityOptions.newBuilder()\n            .setTaskQueue(downloaded.getHostTaskQueue())\n            // Set the amount a time an activity task can stay in the task queue before its picked\n            // up by a Worker. It allows us to support cases where\n            // the activity worker crashes or restarts before the activity starts execution.\n            // This timeout should be specified only when host specific activity task queues are\n            // used like in this sample.\n            // Note that scheduleToStart timeout is not retryable and retry options will ignore it.\n            // This timeout has to be handled by Workflow code.\n            .setScheduleToStartTimeout(Duration.ofSeconds(10))\n            // Set the max time of a single activity execution attempt.\n            // Activity is going to be executed by a Worker listening to the specified\n            // host task queue. If the activity is started but then the activity worker crashes\n            // for some reason, we want to make sure that it is retried after the specified timeout.\n            // This timeout should be be as short as the longest possible execution of the Activity.\n            .setStartToCloseTimeout(Duration.ofSeconds(2))\n            .setRetryOptions(\n                RetryOptions.newBuilder()\n                    .setInitialInterval(Duration.ofSeconds(1))\n                    .setMaximumAttempts(4)\n                    .setDoNotRetry(IllegalArgumentException.class.getName())\n                    .build())\n            .build();\n    StoreActivities hostSpecificStore =\n        Workflow.newActivityStub(StoreActivities.class, hostActivityOptions);\n\n    // Call processFile activity to zip the file.\n    // Call the activity to process the file using worker-specific task queue.\n    String processed = hostSpecificStore.process(downloaded.getFileName());\n    // Call upload activity to upload the zipped file.\n    hostSpecificStore.upload(processed, destination);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/README.md",
    "content": "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.\n\n####  Running the File Processing Sample\n\nThe sample has two executables. Execute each command in a separate terminal window.\n\n\nThe 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.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.fileprocessing.FileProcessingWorker\n```\n\nThe second command start the Workflow Execution. Each time the command runs, it starts a new Workflow Execution.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.fileprocessing.FileProcessingStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/StoreActivities.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport io.temporal.activity.ActivityInterface;\nimport java.net.URL;\n\n@ActivityInterface\npublic interface StoreActivities {\n\n  final class TaskQueueFileNamePair {\n    private String hostTaskQueue;\n    private String fileName;\n\n    public TaskQueueFileNamePair(String hostTaskQueue, String fileName) {\n      this.hostTaskQueue = hostTaskQueue;\n      this.fileName = fileName;\n    }\n\n    /** Jackson needs it */\n    public TaskQueueFileNamePair() {}\n\n    public String getHostTaskQueue() {\n      return hostTaskQueue;\n    }\n\n    public String getFileName() {\n      return fileName;\n    }\n  }\n\n  /**\n   * Upload file to remote location.\n   *\n   * @param localFileName file to upload\n   * @param url remote location\n   */\n  void upload(String localFileName, URL url);\n\n  /**\n   * Process file.\n   *\n   * @param inputFileName source file name @@return processed file name\n   */\n  String process(String inputFileName);\n\n  /**\n   * Downloads file to local disk.\n   *\n   * @param url remote file location\n   * @return local task queue and downloaded file name\n   */\n  TaskQueueFileNamePair download(URL url);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/fileprocessing/StoreActivitiesImpl.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport com.google.common.io.Files;\nimport com.google.common.io.Resources;\nimport io.temporal.activity.Activity;\nimport java.io.File;\nimport java.io.IOException;\nimport java.net.URL;\n\n/** Store activities implementation. */\npublic class StoreActivitiesImpl implements StoreActivities {\n\n  private final String hostSpecificTaskQueue;\n\n  public StoreActivitiesImpl(String taskQueue) {\n    this.hostSpecificTaskQueue = taskQueue;\n  }\n\n  @Override\n  @SuppressWarnings(\"deprecation\")\n  public TaskQueueFileNamePair download(URL url) {\n    try {\n      byte[] binary = Resources.toByteArray(url);\n      File destination = new File(Files.createTempDir(), \"downloaded\");\n      Files.write(binary, destination);\n      System.out.println(\n          \"download activity: downloaded from \" + url + \" to \" + destination.getAbsolutePath());\n      return new TaskQueueFileNamePair(hostSpecificTaskQueue, destination.getAbsolutePath());\n    } catch (IOException e) {\n      throw Activity.wrap(e);\n    }\n  }\n\n  @Override\n  public String process(String sourceFile) {\n    System.out.println(\"process activity: sourceFile= \" + sourceFile);\n    try {\n      String processedName = processFileImpl(sourceFile);\n      System.out.println(\"process activity: processed file: \" + processedName);\n      return processedName;\n    } catch (IOException e) {\n      throw Activity.wrap(e);\n    }\n  }\n\n  @SuppressWarnings(\"deprecation\")\n  private String processFileImpl(String fileName) throws IOException {\n    File inputFile = new File(fileName);\n    File inputDir = inputFile.getParentFile();\n    File outputFile = new File(inputDir, \"processed\");\n    // We don't really process it, just copy to keep the sample simple.\n    Files.copy(inputFile, outputFile);\n    return outputFile.getAbsolutePath();\n  }\n\n  @Override\n  public void upload(String localFileName, URL url) {\n    File file = new File(localFileName);\n    if (!file.isFile()) {\n      throw new IllegalArgumentException(\"Invalid file type: \" + file);\n    }\n    // Faking upload to simplify sample implementation.\n    System.out.println(\"upload activity: uploaded from \" + localFileName + \" to \" + url);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/getresultsasync/MyWorkflow.java",
    "content": "package io.temporal.samples.getresultsasync;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String justSleep(int seconds);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/getresultsasync/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.getresultsasync;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n  @Override\n  public String justSleep(int seconds) {\n    Workflow.sleep(Duration.ofSeconds(seconds));\n    return \"woke up after \" + seconds + \" seconds\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/getresultsasync/README.md",
    "content": "# Get Workflow results async\n\nThis sample shows the use of WorkflowStub.getResult and WorkflowStub.getResultAsync\nto show how the Temporal Client API can not only start Workflows async but also wait for their results \nasync as well.\n\n## Run the sample\n\n1. Start the Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.getresultsasync.Worker\n```\n\n2. Start the Starter\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.getresultsasync.Starter\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/getresultsasync/Starter.java",
    "content": "package io.temporal.samples.getresultsasync;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport java.util.concurrent.TimeUnit;\n\npublic class Starter {\n\n  /**\n   * Show the use and difference between getResult and getResultAsync for waiting on workflow\n   * results.\n   */\n  @SuppressWarnings(\"FutureReturnValueIgnored\")\n  public static void main(String[] args) {\n    MyWorkflow workflowStub1 =\n        Worker.client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(Worker.TASK_QUEUE_NAME).build());\n\n    MyWorkflow workflowStub2 =\n        Worker.client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(Worker.TASK_QUEUE_NAME).build());\n\n    // Start workflow async (not blocking thread)\n    WorkflowClient.start(workflowStub1::justSleep, 3);\n    WorkflowStub untypedStub1 = WorkflowStub.fromTyped(workflowStub1);\n\n    // Get the results, waiting for workflow to complete\n    String result1 = untypedStub1.getResult(String.class); // blocking call, waiting to complete\n    System.out.println(\"Result1: \" + result1);\n\n    // Start the workflow again (async)\n    WorkflowClient.start(workflowStub2::justSleep, 5);\n    WorkflowStub untypedStub2 = WorkflowStub.fromTyped(workflowStub2);\n\n    // getResultAsync returns a CompletableFuture\n    // It is not a blocking call like getResult(...)\n    untypedStub2\n        .getResultAsync(String.class)\n        .thenApply(\n            result2 -> {\n              System.out.println(\"Result2: \" + result2);\n              return result2;\n            });\n\n    System.out.println(\"Waiting on result2...\");\n    // Our workflow sleeps for 5 seconds (async)\n    // Here we block the thread (Thread.sleep) for 7 (2 more than the workflow exec time)\n    // To show that getResultsAsync completion happens during this time (async)\n    sleep(7);\n    System.out.println(\"Done waiting on result2...\");\n    System.exit(0);\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException e) {\n      System.out.println(\"Exception: \" + e.getMessage());\n      System.exit(0);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/getresultsasync/Worker.java",
    "content": "package io.temporal.samples.getresultsasync;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class Worker {\n  public static final WorkflowServiceStubs service;\n  public static final WorkflowClient client;\n  public static final WorkerFactory factory;\n\n  static {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    factory = WorkerFactory.newInstance(client);\n  }\n\n  public static final String TASK_QUEUE_NAME = \"asyncstartqueue\";\n\n  public static void main(String[] args) {\n    io.temporal.worker.Worker worker = factory.newWorker(TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloAccumulator.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.BatchRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowNotFoundException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.io.Serializable;\nimport java.time.Duration;\nimport java.util.ArrayDeque;\nimport java.util.Deque;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.Random;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/*\n * Sample Temporal Workflow Definition that accumulates events.\n * This sample implements the Accumulator Pattern: collect many meaningful\n *  things that need to be collected and worked on together, such as\n *  all payments for an account, or all account updates by account.\n *\n * This sample models robots being created throughout the time period,\n *  groups them by what color they are, and greets all the robots\n *  of a color at the end.\n *\n * A new workflow is created per grouping. Workflows continue as new as needed.\n *  A sample activity at the end is given, and you could add an activity to\n *  process individual events in the processGreeting() method.\n */\npublic class HelloAccumulator {\n  // set a time to wait for another signal to come in, e.g.\n  // Duration.ofDays(30);\n  static final Duration MAX_AWAIT_TIME = Duration.ofMinutes(1);\n\n  static final String TASK_QUEUE = \"HelloAccumulatorTaskQueue\";\n  static final String WORKFLOW_ID_PREFIX = \"HelloAccumulatorWorkflow\";\n\n  public static class Greeting implements Serializable {\n    String greetingText;\n    String bucket;\n    String greetingKey;\n\n    public String getGreetingText() {\n      return greetingText;\n    }\n\n    public void setGreetingText(String greetingText) {\n      this.greetingText = greetingText;\n    }\n\n    public String getBucket() {\n      return bucket;\n    }\n\n    public void setBucket(String bucket) {\n      this.bucket = bucket;\n    }\n\n    public String getGreetingKey() {\n      return greetingKey;\n    }\n\n    public void setGreetingKey(String greetingKey) {\n      this.greetingKey = greetingKey;\n    }\n\n    public Greeting(String greetingText, String bucket, String greetingKey) {\n      this.greetingText = greetingText;\n      this.bucket = bucket;\n      this.greetingKey = greetingKey;\n    }\n\n    public Greeting() {}\n\n    @Override\n    public String toString() {\n      return \"Greeting [greetingText=\"\n          + greetingText\n          + \", bucket=\"\n          + bucket\n          + \", greetingKey=\"\n          + greetingKey\n          + \"]\";\n    }\n  }\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface AccumulatorWorkflow {\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String accumulateGreetings(\n        String bucketKey, Deque<Greeting> greetings, Set<String> allGreetingsSet);\n\n    // Define the workflow sendGreeting signal method. This method is executed when\n    // the workflow receives a greeting signal.\n    @SignalMethod\n    void sendGreeting(Greeting greeting);\n\n    // Define the workflow exit signal method. This method is executed when the\n    // workflow receives an exit signal.\n    @SignalMethod\n    void exit();\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    String composeGreeting(Deque<Greeting> greetings);\n  }\n\n  /** Simple activity implementation. */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n\n    // here is where we process all of the signals together\n    @Override\n    public String composeGreeting(Deque<Greeting> greetings) {\n      List<String> greetingList =\n          greetings.stream().map(u -> u.greetingText).collect(Collectors.toList());\n      return \"Hello (\" + greetingList.size() + \") robots: \" + greetingList + \"!\";\n    }\n  }\n\n  // Main workflow method\n  public static class AccumulatorWorkflowImpl implements AccumulatorWorkflow {\n\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    private static final Logger logger = LoggerFactory.getLogger(AccumulatorWorkflowImpl.class);\n    String bucketKey;\n    ArrayDeque<Greeting> greetings;\n    HashSet<String> allGreetingsSet;\n    boolean exitRequested = false;\n    ArrayDeque<Greeting> unprocessedGreetings = new ArrayDeque<Greeting>();\n\n    @Override\n    public String accumulateGreetings(\n        String bucketKeyInput, Deque<Greeting> greetingsInput, Set<String> allGreetingsSetInput) {\n      bucketKey = bucketKeyInput;\n      greetings = new ArrayDeque<Greeting>();\n      allGreetingsSet = new HashSet<String>();\n      greetings.addAll(greetingsInput);\n      allGreetingsSet.addAll(allGreetingsSetInput);\n\n      // If you want to wait for a fixed amount of time instead of a time after a\n      // message\n      // as this does now, you might want to check out\n      // ../../updatabletimer\n\n      // Main Workflow Loop:\n      // - wait for signals to come in\n      // - every time a signal comes in, wait again for MAX_AWAIT_TIME\n      // - if time runs out, and there are no messages, process them all and exit\n      // - if exit signal is received, process any remaining signals and exit\n      do {\n\n        boolean timedout =\n            !Workflow.await(MAX_AWAIT_TIME, () -> !unprocessedGreetings.isEmpty() || exitRequested);\n\n        while (!unprocessedGreetings.isEmpty()) {\n          processGreeting(unprocessedGreetings.removeFirst());\n        }\n\n        if (exitRequested || timedout) {\n          String greetEveryone = processGreetings(greetings);\n\n          if (unprocessedGreetings.isEmpty()) {\n            logger.info(\"Greeting queue is still empty\");\n            return greetEveryone;\n          } else {\n            // you can get here if you send a signal after an exit, causing rollback just\n            // after the\n            // last processed activity\n            logger.info(\"Greeting queue not empty, looping\");\n          }\n        }\n      } while (!unprocessedGreetings.isEmpty() || !Workflow.getInfo().isContinueAsNewSuggested());\n\n      logger.info(\"starting continue as new processing\");\n\n      // Create a workflow stub that will be used to continue this workflow as a new\n      AccumulatorWorkflow continueAsNew = Workflow.newContinueAsNewStub(AccumulatorWorkflow.class);\n\n      // Request that the new run will be invoked by the Temporal system:\n      continueAsNew.accumulateGreetings(bucketKey, greetings, allGreetingsSet);\n      // this could be improved in the future with the are_handlers_finished API. For\n      // now if a signal comes in\n      // after this, it will fail the workflow task and retry handling the new\n      // signal(s)\n\n      return \"continued as new; results passed to next run\";\n    }\n\n    // Here is where we can process individual signals as they come in.\n    // It's ok to call activities here.\n    // This also validates an individual greeting:\n    // - check for duplicates\n    // - check for correct bucket\n    public void processGreeting(Greeting greeting) {\n      logger.info(\"processing greeting-\" + greeting);\n      if (greeting == null) {\n        logger.warn(\"Greeting is null:\" + greeting);\n        return;\n      }\n\n      // this just ignores incorrect buckets - you can use workflowupdate to validate\n      // and reject\n      // bad bucket requests if needed\n      if (!greeting.bucket.equals(bucketKey)) {\n        logger.warn(\"wrong bucket, something is wrong with your signal processing: \" + greeting);\n        return;\n      }\n\n      if (!allGreetingsSet.add(greeting.greetingKey)) {\n        logger.info(\"Duplicate signal event: \" + greeting.greetingKey);\n        return;\n      }\n\n      // add in any desired event processing activity here\n      greetings.add(greeting);\n    }\n\n    private String processGreetings(Deque<Greeting> greetings) {\n      logger.info(\"Composing greetings for: \" + greetings);\n      return activities.composeGreeting(greetings);\n    }\n\n    // Signal method\n    // Keep it simple, these should be fast and not call activities\n    @Override\n    public void sendGreeting(Greeting greeting) {\n      // signals can be the first workflow code that runs, make sure we have\n      // an ArrayDeque to write to\n      if (unprocessedGreetings == null) {\n        unprocessedGreetings = new ArrayDeque<Greeting>();\n      }\n      logger.info(\"received greeting-\" + greeting);\n      unprocessedGreetings.add(greeting);\n    }\n\n    @Override\n    public void exit() {\n      logger.info(\"exit signal received\");\n      exitRequested = true;\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws Exception {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    client.getWorkflowServiceStubs().healthCheck();\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a\n     * specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue\n     * and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(AccumulatorWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless\n     * and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n\n    // setup which tests to run\n    // by default it will run an accumulation with a few (20) signals\n    // to a set of 4 buckets with Signal To Start\n    boolean testContinueAsNew = false;\n\n    boolean testSignalEdgeCases = true;\n    // configure signal edge cases to test\n    boolean testSignalAfterWorkflowExit = true;\n    boolean testSignalAfterExitSignal = !testSignalAfterWorkflowExit;\n    boolean testDuplicate = true;\n    boolean testIgnoreBadBucket = true;\n\n    // setup to send signals\n    String bucket = \"blue\";\n    String workflowId = WORKFLOW_ID_PREFIX + \"-\" + bucket;\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    String greetingKey = \"key-\";\n    String greetingText = \"Robby Robot\";\n    Greeting starterGreeting = new Greeting(greetingText, bucket, greetingKey);\n    final String[] buckets = {\"red\", \"blue\", \"green\", \"yellow\"};\n    final String[] names = {\"Genghis Khan\", \"Missy\", \"Bill\", \"Ted\", \"Rufus\", \"Abe\"};\n\n    // Create the workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(workflowId).build();\n    AccumulatorWorkflow workflow =\n        client.newWorkflowStub(AccumulatorWorkflow.class, workflowOptions);\n\n    // send many signals to start several workflows\n    int max_signals = 20;\n\n    if (testContinueAsNew) max_signals = 10000;\n    for (int i = 0; i < max_signals; i++) {\n      Random randomBucket = new Random();\n      int bucketIndex = randomBucket.nextInt(buckets.length);\n      bucket = buckets[bucketIndex];\n      starterGreeting.setBucket(bucket);\n      Thread.sleep(20); // simulate some delay\n\n      workflowId = WORKFLOW_ID_PREFIX + \"-\" + bucket;\n\n      Random randomName = new Random();\n      int nameIndex = randomName.nextInt(names.length);\n      starterGreeting.setGreetingText(names[nameIndex] + \" Robot\");\n\n      workflowOptions =\n          WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(workflowId).build();\n\n      // Create the workflow client stub. It is used to start the workflow execution.\n      workflow = client.newWorkflowStub(AccumulatorWorkflow.class, workflowOptions);\n\n      BatchRequest request = client.newSignalWithStartRequest();\n      starterGreeting.greetingKey = greetingKey + i;\n      request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n      request.add(workflow::sendGreeting, starterGreeting);\n      client.signalWithStart(request);\n    }\n\n    // Demonstrate we still can connect to WF and get result using untyped:\n    if (max_signals > 0) {\n      WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n\n      // wait for it to finish\n      try {\n        String greeting = untyped.getResult(String.class);\n        printWorkflowStatus(client, workflowId);\n        System.out.println(\"Greeting: \" + greeting);\n      } catch (WorkflowFailedException e) {\n        System.out.println(\"Workflow failed: \" + e.getCause().getMessage());\n        printWorkflowStatus(client, workflowId);\n      }\n    }\n    if (!testSignalEdgeCases) {\n      System.exit(0); // skip other demonstrations below\n    }\n\n    // set workflow parameters\n    bucket = \"purple\";\n    greetingList = new ArrayDeque<Greeting>();\n    allGreetingsSet = new HashSet<String>();\n    workflowId = WORKFLOW_ID_PREFIX + \"-\" + bucket;\n    workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(workflowId).build();\n\n    starterGreeting = new Greeting(\"Suzy Robot\", bucket, \"11235813\");\n\n    // Create the workflow client stub. It is used to start the workflow execution.\n    AccumulatorWorkflow workflowSync =\n        client.newWorkflowStub(AccumulatorWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously and call its getGreeting workflow method\n    WorkflowClient.start(workflowSync::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    // After start for accumulateGreetings returns, the workflow is guaranteed to be\n    // started, so we can send a signal to it using the workflow stub.\n    // This workflow keeps receiving signals until exit is called or the timer\n    // finishes with no\n    // signals\n\n    // When the workflow is started the accumulateGreetings will block for the\n    // previously defined conditions\n    // Send the first workflow signal\n    workflowSync.sendGreeting(starterGreeting);\n\n    // Test sending an exit, waiting for workflow exit, then sending a signal.\n    // This will trigger a WorkflowNotFoundException if using the same workflow\n    // handle\n    if (testSignalAfterWorkflowExit) {\n      workflowSync.exit();\n      String greetingsAfterExit =\n          workflowSync.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n      System.out.println(greetingsAfterExit);\n    }\n\n    // Test sending an exit, not waiting for workflow to exit, then sending a signal\n    // this demonstrates Temporal history rollback\n    // see https://community.temporal.io/t/continueasnew-signals/1008/7\n    if (testSignalAfterExitSignal) {\n      workflowSync.exit();\n    }\n\n    // Test sending more signals after workflow exit\n    try {\n      // send a second workflow signal\n      Greeting janeGreeting = new Greeting(\"Jane Robot\", bucket, \"112358132134\");\n      workflowSync.sendGreeting(janeGreeting);\n\n      if (testIgnoreBadBucket) {\n        // send a third signal with an incorrect bucket - this will be ignored\n        // can use workflow update to validate and reject a request if needed\n        workflowSync.sendGreeting(new Greeting(\"Sally Robot\", \"taupe\", \"112358132134\"));\n      }\n\n      if (testDuplicate) {\n        // intentionally send a duplicate signal\n        workflowSync.sendGreeting(janeGreeting);\n      }\n\n      if (!testSignalAfterWorkflowExit) {\n        // wait for results if we haven't waited for them yet\n        String greetingsAfterExit =\n            workflowSync.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n        System.out.println(greetingsAfterExit);\n      }\n    } catch (WorkflowNotFoundException e) {\n      System.out.println(\"Workflow not found - this is intentional: \" + e.getCause().getMessage());\n      printWorkflowStatus(client, workflowId);\n    }\n\n    try {\n      /*\n       * Here we create a new workflow stub using the same workflow id.\n       * We do this to demonstrate that to send a signal to an already running\n       * workflow you only need to know its workflow id.\n       */\n      AccumulatorWorkflow workflowById =\n          client.newWorkflowStub(AccumulatorWorkflow.class, workflowId);\n\n      Greeting laterGreeting = new Greeting(\"XVX Robot\", bucket, \"1123581321\");\n      // Send the second signal to our workflow\n      workflowById.sendGreeting(laterGreeting);\n\n      // Now let's send our exit signal to the workflow\n      workflowById.exit();\n\n      /*\n       * We now call our accumulateGreetings workflow method synchronously after our\n       * workflow has started.\n       * This reconnects our workflowById workflow stub to the existing workflow and\n       * blocks until a result is available. Note that this behavior assumes that\n       * WorkflowOptions\n       * are not configured with WorkflowIdReusePolicy.AllowDuplicate. If they were,\n       * this call would fail\n       * with the WorkflowExecutionAlreadyStartedException exception.\n       * You can use the policy to force workflows for a new time period, e.g. a\n       * collection day, to have a new workflow ID.\n       */\n\n      String greetings = workflowById.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n\n      // Print our results for greetings which were sent by signals\n      System.out.println(greetings);\n    } catch (WorkflowNotFoundException e) {\n      System.out.println(\"Workflow not found - this is intentional: \" + e.getCause().getMessage());\n      printWorkflowStatus(client, workflowId);\n    }\n\n    /*\n     * Here we try to send the signals as start to demonstrate that after a workflow\n     * exited\n     * and signals failed to send\n     * we can send signals to a new workflow\n     */\n    if (testSignalAfterWorkflowExit) {\n      greetingList = new ArrayDeque<Greeting>();\n      allGreetingsSet = new HashSet<String>();\n      workflowId = WORKFLOW_ID_PREFIX + \"-\" + bucket;\n\n      Greeting laterGreeting = new Greeting(\"Final Robot\", bucket, \"1123\");\n      // Send the second signal to our workflow\n\n      workflowOptions =\n          WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(workflowId).build();\n\n      // Create the workflow client stub. It is used to start the workflow execution.\n      workflow = client.newWorkflowStub(AccumulatorWorkflow.class, workflowOptions);\n\n      BatchRequest request = client.newSignalWithStartRequest();\n      request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n      request.add(workflow::sendGreeting, laterGreeting);\n      client.signalWithStart(request);\n\n      printWorkflowStatus(client, workflowId);\n\n      String greetingsAfterExit =\n          workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n\n      // Print our results for greetings which were sent by signals\n      System.out.println(greetingsAfterExit);\n\n      printWorkflowStatus(client, workflowId);\n\n      while (getWorkflowStatus(client, workflowId).equals(\"WORKFLOW_EXECUTION_STATUS_RUNNING\")) {\n\n        System.out.println(\"Workflow still running \");\n        Thread.sleep(1000);\n      }\n    }\n\n    System.exit(0);\n  }\n\n  private static void printWorkflowStatus(WorkflowClient client, String workflowId) {\n    WorkflowStub existingUntyped =\n        client.newUntypedWorkflowStub(workflowId, Optional.empty(), Optional.empty());\n    DescribeWorkflowExecutionRequest describeWorkflowExecutionRequest =\n        DescribeWorkflowExecutionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setExecution(existingUntyped.getExecution())\n            .build();\n\n    DescribeWorkflowExecutionResponse resp =\n        client\n            .getWorkflowServiceStubs()\n            .blockingStub()\n            .describeWorkflowExecution(describeWorkflowExecutionRequest);\n    System.out.println(\n        \"**** PARENT: \" + resp.getWorkflowExecutionInfo().getParentExecution().getWorkflowId());\n\n    WorkflowExecutionInfo workflowExecutionInfo = resp.getWorkflowExecutionInfo();\n    System.out.println(\"Workflow Status: \" + workflowExecutionInfo.getStatus().toString());\n  }\n\n  private static String getWorkflowStatus(WorkflowClient client, String workflowId) {\n    WorkflowStub existingUntyped =\n        client.newUntypedWorkflowStub(workflowId, Optional.empty(), Optional.empty());\n    DescribeWorkflowExecutionRequest describeWorkflowExecutionRequest =\n        DescribeWorkflowExecutionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setExecution(existingUntyped.getExecution())\n            .build();\n\n    DescribeWorkflowExecutionResponse resp =\n        client\n            .getWorkflowServiceStubs()\n            .blockingStub()\n            .describeWorkflowExecution(describeWorkflowExecutionRequest);\n    System.out.println(\n        \"**** PARENT: \" + resp.getWorkflowExecutionInfo().getParentExecution().getWorkflowId());\n\n    WorkflowExecutionInfo workflowExecutionInfo = resp.getWorkflowExecutionInfo();\n    return workflowExecutionInfo.getStatus().toString();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloActivity.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/** Sample Temporal Workflow Definition that executes a single Activity. */\npublic class HelloActivity {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloActivityTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloActivityWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    @ActivityMethod(name = \"greet\")\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * overall timeout that our workflow is willing to wait for activity to complete. For this\n     * example it is set to 2 seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /** Simple activity implementation, that concatenates two strings. */\n  public static class GreetingActivitiesImpl implements GreetingActivities {\n    private static final Logger log = LoggerFactory.getLogger(GreetingActivitiesImpl.class);\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      log.info(\"Composing greeting...\");\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     *\n     * See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloActivityExclusiveChoice.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Sample Temporal Workflow Definition demonstrating how to execute an Activity based on dynamic\n * input.\n */\npublic class HelloActivityExclusiveChoice {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloActivityChoiceTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloActivityChoiceWorkflow\";\n\n  // Different fruits you can order\n  enum Fruits {\n    APPLE,\n    BANANA,\n    CHERRY,\n    ORANGE\n  }\n\n  // Our example shopping list for different fruits\n  public static class ShoppingList {\n    private Map<Fruits, Integer> list = new HashMap<>();\n\n    public void addFruitOrder(Fruits fruit, int amount) {\n      list.put(fruit, amount);\n    }\n\n    public Map<Fruits, Integer> getList() {\n      return list;\n    }\n  }\n\n  /**\n   * Define the Workflow Interface. It must contain at least one method annotated\n   * with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface PurchaseFruitsWorkflow {\n\n    /**\n     * Define the workflow method. This method is executed when the workflow is started. The\n     * workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    StringBuilder orderFruit(ShoppingList list);\n  }\n\n  /**\n   * Define the Activity Interface. Workflow methods can call activities during execution.\n   * Annotating activity methods with @ActivityMethod is optional\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface OrderFruitsActivities {\n    // Define your activity methods which can be called during workflow execution\n\n    String orderApples(int amount);\n\n    String orderBananas(int amount);\n\n    String orderCherries(int amount);\n\n    String orderOranges(int amount);\n  }\n\n  // Define the workflow implementation. It implements our orderFruit workflow method\n  public static class PurchaseFruitsWorkflowImpl implements PurchaseFruitsWorkflow {\n\n    /*\n     * Define the OrderActivities stub. Activity stubs implements activity interfaces and proxy\n     * calls to it to Temporal activity invocations. Since Temporal activities are reentrant, a\n     * single activity stub can be used for multiple activity invocations.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    private final OrderFruitsActivities activities =\n        Workflow.newActivityStub(\n            OrderFruitsActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public StringBuilder orderFruit(ShoppingList list) {\n      StringBuilder shoppingResults = new StringBuilder();\n      // Go through each element of our shopping list\n      list.getList()\n          .forEach(\n              (fruit, amount) -> {\n                // You can use a basic switch to call an activity method based on the workflow input\n                switch (fruit) {\n                  case APPLE:\n                    shoppingResults.append(activities.orderApples(amount));\n                    break;\n                  case BANANA:\n                    shoppingResults.append(activities.orderBananas(amount));\n                    break;\n                  case CHERRY:\n                    shoppingResults.append(activities.orderCherries(amount));\n                    break;\n                  case ORANGE:\n                    shoppingResults.append(activities.orderOranges(amount));\n                    break;\n                  default:\n                    shoppingResults.append(\"Unable to order fruit: \").append(fruit);\n                    break;\n                }\n              });\n      return shoppingResults;\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined activity methods.\n   */\n  static class OrderFruitsActivitiesImpl implements OrderFruitsActivities {\n    @Override\n    public String orderApples(int amount) {\n      return \"Ordered \" + amount + \" Apples...\";\n    }\n\n    @Override\n    public String orderBananas(int amount) {\n      return \"Ordered \" + amount + \" Bananas...\";\n    }\n\n    @Override\n    public String orderCherries(int amount) {\n      return \"Ordered \" + amount + \" Cherries...\";\n    }\n\n    @Override\n    public String orderOranges(int amount) {\n      return \"Ordered \" + amount + \" Oranges...\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method is our\n   * workflow starter.\n   */\n  public static void main(String[] args) {\n    /*\n     * Define the workflow service. It is a gRPC stubs wrapper which talks to the docker instance of\n     * our locally running Temporal service.\n     */\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker. Since workflows are stateful in nature,\n     * we need to register our workflow type.\n     */\n    worker.registerWorkflowImplementationTypes(PurchaseFruitsWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new OrderFruitsActivitiesImpl());\n\n    // Start all the workers registered for a specific task queue.\n    factory.start();\n\n    // Create our workflow client stub. It is used to start our workflow execution.\n    PurchaseFruitsWorkflow workflow =\n        client.newWorkflowStub(\n            PurchaseFruitsWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Let's build our example shopping list\n    ShoppingList shoppingList = new ShoppingList();\n    shoppingList.addFruitOrder(Fruits.APPLE, 8);\n    shoppingList.addFruitOrder(Fruits.BANANA, 5);\n    shoppingList.addFruitOrder(Fruits.CHERRY, 1);\n    shoppingList.addFruitOrder(Fruits.ORANGE, 4);\n\n    // Execute our workflow method\n    StringBuilder orderResults = workflow.orderFruit(shoppingList);\n\n    System.out.println(\"Order results: \" + orderResults);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloActivityRetry.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample Temporal workflow that demonstrates workflow activity retries. */\npublic class HelloActivityRetry {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloActivityWithRetriesTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloActivityWithRetriesWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    /** Define your activity method which can be called during workflow execution */\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setInitialInterval\" option sets the\n     * interval of the first retry. It is set to 1 second. The \"setDoNotRetry\" option is a list of\n     * application failures for which retries should not be performed.\n     *\n     * <p>By default the maximum number of retry attempts is set to \"unlimited\" however you can\n     * change it by adding the \"setMaximumAttempts\" option to the retry options.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(10))\n                .setRetryOptions(\n                    RetryOptions.newBuilder()\n                        .setInitialInterval(Duration.ofSeconds(1))\n                        .setDoNotRetry(IllegalArgumentException.class.getName())\n                        .build())\n                .build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after activity is completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    private int callCount;\n    private long lastInvocationTime;\n\n    /**\n     * Our activity implementation simulates a failure 3 times. Given our previously set\n     * RetryOptions, our workflow is going to retry our activity execution.\n     */\n    @Override\n    public synchronized String composeGreeting(String greeting, String name) {\n      if (lastInvocationTime != 0) {\n        long timeSinceLastInvocation = System.currentTimeMillis() - lastInvocationTime;\n        System.out.print(timeSinceLastInvocation + \" milliseconds since last invocation. \");\n      }\n      lastInvocationTime = System.currentTimeMillis();\n      if (++callCount < 4) {\n        System.out.println(\"composeGreeting activity is going to fail\");\n\n        /*\n         * We throw IllegalStateException here. It is not in the list of \"do not retry\" exceptions\n         * set in our RetryOptions, so a workflow retry is going to be issued\n         */\n        throw new IllegalStateException(\"not yet\");\n      }\n\n      // after 3 unsuccessful retries we finally can complete our activity execution\n      System.out.println(\"composeGreeting activity is going to complete\");\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Set our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     *\n     * See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloAsync.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample Temporal Workflow Definition that demonstrates an asynchronous Activity Execution. */\npublic class HelloAsync {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloAsyncActivityTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloAsyncActivityWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /*\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public String getGreeting(String name) {\n\n      /*\n       * This is our workflow method. We invoke the composeGreeting method two times using\n       * {@link io.temporal.workflow.Async#function(Func)}.\n       * The results of each async activity method invocation returns us a\n       * {@link io.temporal.workflow.Promise} which is similar to a Java {@link java.util.concurrent.Future}\n       */\n      Promise<String> hello = Async.function(activities::composeGreeting, \"Hello\", name);\n      Promise<String> bye = Async.function(activities::composeGreeting, \"Bye\", name);\n\n      // After calling the two activity methods async, we block until we receive their results\n      return hello.get() + \"\\n\" + bye.get();\n    }\n  }\n\n  /** Simple activity implementation, that concatenates two strings. */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Execute our workflow and wait for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloAsyncActivityCompletion.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.*;\nimport io.temporal.client.ActivityCompletionClient;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport java.util.concurrent.ForkJoinPool;\n\n/** Sample Temporal Workflow Definition that demonstrates asynchronous Activity Execution */\npublic class HelloAsyncActivityCompletion {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloAsyncActivityCompletionTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloAsyncActivityCompletionWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    /** Define the activity method which can be called during workflow execution */\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements the getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /*\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites the defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n\n    /*\n     * ActivityCompletionClient is used to asynchronously complete activities. In this example we\n     * will use this client alongside with {@link\n     * io.temporal.activity.ActivityExecutionContext#doNotCompleteOnReturn()} which means our\n     * activity method will not complete when it returns, however is expected to be completed\n     * asynchronously using the client.\n     */\n    private final ActivityCompletionClient completionClient;\n\n    GreetingActivitiesImpl(ActivityCompletionClient completionClient) {\n      this.completionClient = completionClient;\n    }\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n\n      // Get the activity execution context\n      ActivityExecutionContext context = Activity.getExecutionContext();\n\n      // Set a correlation token that can be used to complete the activity asynchronously\n      byte[] taskToken = context.getTaskToken();\n\n      /*\n       * For the example we will use a {@link java.util.concurrent.ForkJoinPool} to execute our\n       * activity. In real-life applications this could be any service. The composeGreetingAsync\n       * method is the one that will actually complete workflow action execution.\n       */\n      ForkJoinPool.commonPool().execute(() -> composeGreetingAsync(taskToken, greeting, name));\n      context.doNotCompleteOnReturn();\n\n      // Since we have set doNotCompleteOnReturn(), the workflow action method return value is\n      // ignored.\n      return \"ignored\";\n    }\n\n    // Method that will complete action execution using the defined ActivityCompletionClient\n    private void composeGreetingAsync(byte[] taskToken, String greeting, String name) {\n      String result = greeting + \" \" + name + \"!\";\n\n      // Complete our workflow activity using ActivityCompletionClient\n      completionClient.complete(taskToken, result);\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws ExecutionException, InterruptedException {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our Workflow Types with the Worker. Workflow Types must be known to the Worker at\n     * runtime in order for it to poll for Workflow Tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    ActivityCompletionClient completionClient = client.newActivityCompletionClient();\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl(completionClient));\n\n    /*\n     * Start all the Workers registered for a specific Task Queue. The Workers then start polling\n     * for Workflow Tasks and Activity Tasks.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Here we use {@link io.temporal.client.WorkflowClient} to execute our workflow asynchronously.\n     * It gives us back a {@link java.util.concurrent.CompletableFuture}. We can then call its get\n     * method to block and wait until a result is available.\n     */\n    CompletableFuture<String> greeting = WorkflowClient.execute(workflow::getGreeting, \"World\");\n\n    // Wait for workflow execution to complete and display its results.\n    System.out.println(greeting.get());\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloAsyncLambda.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample Temporal Workflow Definition that demonstrates an asynchronous Activity Executions. */\npublic class HelloAsyncLambda {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloAsyncLambdaTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloAsyncLambdaWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    String getGreeting();\n\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /*\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public String getGreeting(String name) {\n\n      /*\n       * Here we invoke our composeGreeting workflow activity two times asynchronously. For this we\n       * use {@link io.temporal.workflow.Async} which has support for invoking lambdas. Behind the\n       * scenes it allocates a thread to execute each activity method async.\n       */\n      Promise<String> result1 =\n          Async.function(\n              () -> {\n                String greeting = activities.getGreeting();\n                return activities.composeGreeting(greeting, name);\n              });\n      Promise<String> result2 =\n          Async.function(\n              () -> {\n                String greeting = activities.getGreeting();\n                return activities.composeGreeting(greeting, name);\n              });\n\n      // blocking call to wait for our activities to return results\n      return result1.get() + \"\\n\" + result2.get();\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined getGreeting and\n   * composeGreeting methods.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n\n    @Override\n    public String getGreeting() {\n      return \"Hello\";\n    }\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Define our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloAwait.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Sample Temporal workflow that demonstrates how to use workflow await methods to wait up to a\n * specified timeout for a condition updated from a signal handler.\n */\npublic class HelloAwait {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloAwaitTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloAwaitWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see WorkflowInterface\n   * @see WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting();\n\n    // Define the workflow waitForName signal method. This method is executed when the workflow\n    // receives a \"WaitForName\" signal.\n    @SignalMethod\n    void waitForName(String name);\n  }\n\n  // Define the workflow implementation which implements the getGreetings workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    private String name;\n\n    @Override\n    public String getGreeting() {\n      boolean ok = Workflow.await(Duration.ofSeconds(10), () -> name != null);\n      if (ok) {\n        return \"Hello \" + name + \"!\";\n      } else {\n        // To fail workflow use ApplicationFailure. Any other exception would cause workflow to\n        // stall, not to fail.\n        throw ApplicationFailure.newFailure(\n            \"WaitForName signal is not received within 10 seconds.\", \"signal-timeout\");\n      }\n    }\n\n    @Override\n    public void waitForName(String name) {\n      this.name = name;\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws Exception {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(WORKFLOW_ID).build();\n\n    // Create the workflow client stub. It is used to start the workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously and call its getGreeting workflow method\n    WorkflowClient.start(workflow::getGreeting);\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // Send WaitForName signal.\n    workflow.waitForName(\"World\");\n\n    /*\n     * Here we create a new untyped workflow stub using the same workflow id.\n     * The untyped stub is a convenient way to wait for a workflow result.\n     */\n    WorkflowStub workflowById = client.newUntypedWorkflowStub(WORKFLOW_ID);\n\n    String greeting = workflowById.getResult(String.class);\n\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloCancellationScope.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityCancellationType;\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.ActivityCompletionException;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerOptions;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.CancellationScope;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.concurrent.TimeUnit;\n\n/**\n * Sample Temporal Workflow Definition that demonstrates parallel Activity Executions with a\n * Cancellation Scope. When one of the Activity Executions finish, we cancel the execution of the\n * other Activities and wait for their cancellation to complete.\n */\npublic class HelloCancellationScope {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloCancellationScopeTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloCancellationScopeWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    private static final int ACTIVITY_MAX_SLEEP_SECONDS = 30;\n    private static final int ACTIVITY_MAX_CLEANUP_SECONDS = 5;\n    private static final int ACTIVITY_START_TO_CLOSE_TIMEOUT =\n        ACTIVITY_MAX_SLEEP_SECONDS + ACTIVITY_MAX_CLEANUP_SECONDS + 10;\n\n    private static final String[] greetings =\n        new String[] {\"Hello\", \"Bye\", \"Hola\", \"Привет\", \"Oi\", \"Hallo\"};\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     *\n     * <p>The \"setCancellationType\" option means that in case of activity cancellation the activity\n     * should fail with {@link CanceledFailure}. We set\n     * ActivityCancellationType.WAIT_CANCELLATION_COMPLETED which denotes that activity should be\n     * first notified of the cancellation, and cancelled after it can perform some cleanup tasks for\n     * example. Note that an activity must heartbeat to receive cancellation notifications.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder()\n                // if heartbeat timeout is not set, activity heartbeats will be throttled to one\n                // every 30 seconds\n                // which is too rare for the cancellations to be delivered in this example.\n                .setHeartbeatTimeout(Duration.ofSeconds(5))\n                .setStartToCloseTimeout(Duration.ofSeconds(ACTIVITY_START_TO_CLOSE_TIMEOUT))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                .build());\n\n    @Override\n    public String getGreeting(String name) {\n      List<Promise<String>> results = new ArrayList<>(greetings.length);\n\n      /*\n       * Create our CancellationScope. Within this scope we call the workflow activity\n       * composeGreeting method asynchronously for each of our defined greetings in different\n       * languages.\n       */\n      CancellationScope scope =\n          Workflow.newCancellationScope(\n              () -> {\n                for (String greeting : greetings) {\n                  results.add(Async.function(activities::composeGreeting, greeting, name));\n                }\n              });\n\n      /*\n       * Execute all activities within the CancellationScope. Note that this execution is\n       * non-blocking as the code inside our cancellation scope is also non-blocking.\n       */\n      scope.run();\n\n      // We use \"anyOf\" here to wait for one of the activity invocations to return\n      String result = Promise.anyOf(results).get();\n\n      // Trigger cancellation of all uncompleted activity invocations within the cancellation scope\n      scope.cancel();\n\n      /*\n       *  Wait for all activities to perform cleanup if needed.\n       *  For the sake of the example we ignore cancellations and\n       *  get all the results so that we can print them in the end.\n       *\n       *  Note that we cannot use \"allOf\" here as that fails on any Promise failures\n       */\n      for (Promise<String> activityResult : results) {\n        try {\n          activityResult.get();\n        } catch (ActivityFailure e) {\n          if (!(e.getCause() instanceof CanceledFailure)) {\n            throw e;\n          }\n        }\n      }\n      return result;\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n\n      // Get the activity execution context\n      ActivityExecutionContext context = Activity.getExecutionContext();\n\n      // simulate a random time this activity should execute for\n      Random random = new Random();\n      int seconds = random.nextInt(GreetingWorkflowImpl.ACTIVITY_MAX_SLEEP_SECONDS - 5) + 5;\n      System.out.println(\"Activity for \" + greeting + \" going to take \" + seconds + \" seconds\");\n\n      for (int i = 0; i < seconds; i++) {\n        sleep(1);\n        try {\n          // Perform the heartbeat. Used to notify the workflow that activity execution is alive\n          context.heartbeat(i);\n        } catch (ActivityCompletionException e) {\n          /*\n           * Activity heartbeat can throw an exception for multiple reasons, including:\n           * 1) activity cancellation\n           * 2) activity not existing (due to a timeout for example) from the service point of view\n           * 3) activity worker shutdown request\n           *\n           * In our case our activity fails because one of the other performed activities\n           * has completed execution and our workflow method has issued the \"cancel\" request\n           * to cancel all other activities in the cancellation scope.\n           *\n           * The following code simulates our activity after cancellation \"cleanup\"\n           */\n          seconds = random.nextInt(GreetingWorkflowImpl.ACTIVITY_MAX_CLEANUP_SECONDS);\n          System.out.println(\n              \"Activity for \"\n                  + greeting\n                  + \" was cancelled. Cleanup is expected to take \"\n                  + seconds\n                  + \" seconds.\");\n          sleep(seconds);\n          System.out.println(\"Activity for \" + greeting + \" finished cancellation\");\n          throw e;\n        }\n      }\n\n      // return results of activity invocation\n      System.out.println(\"Activity for \" + greeting + \" completed\");\n      return greeting + \" \" + name + \"!\";\n    }\n\n    private void sleep(int seconds) {\n      try {\n        Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n      } catch (InterruptedException ee) {\n        // Empty\n      }\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     *\n     * In the {@link ActivityOptions} definition the\n     * \"setMaxConcurrentActivityExecutionSize\" option sets the max number of parallel activity executions allowed\n     * The \"setMaxConcurrentActivityTaskPollers\" option sets the number of simultaneous poll requests on activity task queue\n     */\n    Worker worker =\n        factory.newWorker(\n            TASK_QUEUE,\n            WorkerOptions.newBuilder()\n                .setMaxConcurrentActivityExecutionSize(100)\n                .setMaxConcurrentActivityTaskPollers(1)\n                .build());\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloCancellationScopeWithTimer.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.*;\nimport io.temporal.client.ActivityCompletionException;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.concurrent.TimeUnit;\n\npublic class HelloCancellationScopeWithTimer {\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloCancellationScopeTimerTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloCancellationScopeTimerWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface CancellationWithTimerWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String execute(String input);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface UpdateInfoActivities {\n    String updateInfo(String input);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class CancellationWithTimerWorkflowImpl implements CancellationWithTimerWorkflow {\n    private final UpdateInfoActivities activities =\n        Workflow.newActivityStub(\n            UpdateInfoActivities.class,\n            ActivityOptions.newBuilder()\n                // If heartbeat timeout is not set, activity heartbeats will be throttled to one\n                // every 30 seconds, it also will not have a heartbeat timeout.\n                .setHeartbeatTimeout(Duration.ofSeconds(2))\n                .setStartToCloseTimeout(Duration.ofSeconds(10))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                .build());\n\n    private String result;\n\n    @Override\n    public String execute(String input) {\n      // Create cancellation scope for our activity execution\n      CancellationScope cancellationScope =\n          Workflow.newCancellationScope(\n              () -> {\n                try {\n                  result = activities.updateInfo(input);\n                } catch (ActivityFailure cause) {\n                  throw cause;\n                }\n              });\n\n      // Create a timer, if this timer fires we want to cancel our activity and complete the\n      // workflow execution\n      // Giving client default result. Note for sample the tier is set to less than the\n      // activity StartToClose timeout in order to simulate it getting cancelled\n      Workflow.newTimer(Duration.ofSeconds(3))\n          .thenApply(\n              result -> {\n                // Cancel our activity, note activity has to heartbeat to receive cancellation\n                System.out.println(\"Cancelling scope as timer fired\");\n                cancellationScope.cancel();\n                return null;\n              });\n      // Start our cancellation scope\n      try {\n        cancellationScope.run();\n      } catch (ActivityFailure e) {\n        // Activity cancellation is going thrigger activity failure\n        // The cause of activity failure would be CanceledFailure\n        if (e.getCause() instanceof CanceledFailure) {\n          result = \"Activity canceled due to timer firing.\";\n        } else {\n          result = \"Activity failed due to: \" + e.getMessage();\n        }\n      }\n      return result;\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * method.\n   */\n  static class UpdateInfoActivitiesImpl implements UpdateInfoActivities {\n\n    @Override\n    public String updateInfo(String input) {\n      // Get the activity execution context\n      ActivityExecutionContext context = Activity.getExecutionContext();\n\n      // Our \"dummy\" activity just sleeps for a second up to 10 times and heartbeats\n      for (int i = 0; i < 10; i++) {\n        sleep(1);\n        try {\n          context.heartbeat(i);\n        } catch (ActivityCompletionException e) {\n          // Here we can do some cleanup if needed before we re-throw activity completion exception\n          throw e;\n        }\n      }\n\n      return \"dummy activity result\";\n    }\n\n    private void sleep(int seconds) {\n      try {\n        Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n      } catch (InterruptedException ee) {\n        // Empty\n      }\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Get a Workflow service stub.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(CancellationWithTimerWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new UpdateInfoActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    CancellationWithTimerWorkflow workflow =\n        client.newWorkflowStub(\n            CancellationWithTimerWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     */\n    String result = workflow.execute(\"Some test input\");\n\n    // Display workflow execution results\n    System.out.println(result);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloChild.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\n\n/**\n * Sample Temporal Workflow Definition that demonstrates the execution of a Child Workflow. Child\n * workflows allow you to group your Workflow logic into small logical and reusable units that solve\n * a particular problem. They can be typically reused by multiple other Workflows.\n */\npublic class HelloChild {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloChildTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloChildWorkflow\";\n\n  /**\n   * Define the parent workflow interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * Define the parent workflow method. This method is executed when the workflow is started. The\n     * workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * Define the child workflow Interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingChild {\n\n    /**\n     * Define the child workflow method. This method is executed when the workflow is started. The\n     * workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the parent workflow implementation. It implements the getGreeting workflow method\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    @Override\n    public String getGreeting(String name) {\n      /*\n       * Define the child workflow stub. Since workflows are stateful,\n       * a new stub must be created for each child workflow.\n       */\n      GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);\n\n      // This is a non blocking call that returns immediately.\n      // Use child.composeGreeting(\"Hello\", name) to call synchronously.\n\n      /*\n       * Invoke the child workflows composeGreeting workflow method.\n       * This call is non-blocking and returns immediately returning a {@link io.temporal.workflow.Promise}\n       *\n       * You can use child.composeGreeting(\"Hello\", name) instead to call the child workflow method synchronously.\n       */\n      Promise<String> greeting = Async.function(child::composeGreeting, \"Hello\", name);\n\n      // Wait for the child workflow to complete and return its results\n      return greeting.get();\n    }\n  }\n\n  /**\n   * Define the parent workflow implementation. It implements the getGreeting workflow method\n   *\n   * <p>Note that a workflow implementation must always be public for the Temporal library to be\n   * able to create its instances.\n   */\n  public static class GreetingChildImpl implements GreetingChild {\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With the workflow, and child workflow defined, we can now start execution. The main method is\n   * the workflow starter.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the worker factory. It is used to create workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the worker. Workers listen to a defined task queue and process workflows and\n     * activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the parent and child workflow implementation with the worker.\n     * Since workflows are stateful in nature,\n     * we need to register the workflow types.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class, GreetingChildImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Start a workflow execution. Usually this is done from another program.\n    // Uses task queue from the GreetingWorkflow @WorkflowMethod annotation.\n\n    // Create our parent workflow client stub. It is used to start the parent workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Execute our parent workflow and wait for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display the parent workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloCron.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowExecutionAlreadyStarted;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Sample Temporal workflow that demonstrates periodic workflow execution using a cron. Note that\n * the periodic execution is based on a fixed delay (provided by the cron definition). To learn\n * about periodic execution with a dynamic delay checkout the {@link HelloPeriodic} example.\n */\npublic class HelloCron {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloCronTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloCronWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    void greet(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    void greet(String greeting);\n  }\n\n  // Define the workflow implementation which implements the greet workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public void greet(String name) {\n      activities.greet(\"Hello \" + name + \"!\");\n    }\n  }\n\n  /**\n   * Implementation of the workflow activity interface. It overwrites the defined greet activity\n   * method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public void greet(String greeting) {\n      System.out.println(\n          \"From \" + Activity.getExecutionContext().getInfo().getWorkflowId() + \": \" + greeting);\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register the workflow activity implementation with the worker. Since workflow activities are\n     * stateless and thread-safe, we need to register a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    /*\n     * Define our workflow options. Note that the cron definition is not part of the\n     * core workflow definition. Workflow options allow you to execute the same\n     * workflow in different ways (for example with or without a cron, etc).\n     *\n     * Here we use setCronSchedule to define a cron for our workflow execution.\n     * The cron format is parsed by the https://github.com/robfig/cron\" library.\n     * In addition to the standard \"* * * * *\" format Temporal also supports the \"@every\" as well as\n     * other cron definition extensions. For example you could define \"@every 2s\" to define a cron definition\n     * which executes our workflow every two seconds.\n     *\n     * The defined cron expression \"* * * * *\" means that our workflow should execute every minute.\n     *\n     * We also use setWorkflowExecutionTimeout to define the workflow execution total time (set to three minutes).\n     * After this time, our workflow execution ends (and our cron will stop executing as well).\n     *\n     * The setWorkflowRunTimeout defines the amount of time after which a single workflow instance is terminated.\n     *\n     * So given all our settings in the WorkflowOptions we define the following:\n     * \"Execute our workflow once every minute for three minutes.\n     * Once a workflow instance is started, terminate it after one minute (if its still running)\"\n     *\n     */\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(WORKFLOW_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setCronSchedule(\"* * * * *\")\n            .setWorkflowExecutionTimeout(Duration.ofMinutes(3))\n            .setWorkflowRunTimeout(Duration.ofMinutes(1))\n            .build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    try {\n      // start workflow execution\n      WorkflowExecution execution = WorkflowClient.start(workflow::greet, \"World\");\n      System.out.println(\"Started \" + execution);\n    } catch (WorkflowExecutionAlreadyStarted e) {\n      // Thrown when a workflow with the same WORKFLOW_ID is currently running\n      System.out.println(\"Already running as \" + e.getExecution());\n    } catch (Throwable e) {\n      e.printStackTrace();\n      System.exit(1);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloDelayedStart.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.WorkflowExecutionHistory;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample Temporal Workflow Definition that shows how to use delayed start. */\npublic class HelloDelayedStart {\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloDelayedStartTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloDelayedStartWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface DelayedStartWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    void start();\n  }\n\n  // Define the workflow implementation which implements our start workflow method.\n  public static class DelayedStartWorkflowImpl implements DelayedStartWorkflow {\n    @Override\n    public void start() {\n      // this workflow just sleeps for a second\n      Workflow.sleep(Duration.ofSeconds(1));\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(DelayedStartWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    DelayedStartWorkflow workflow =\n        client.newWorkflowStub(\n            DelayedStartWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                // set delayed start in 2 seconds\n                .setStartDelay(Duration.ofSeconds(2))\n                .build());\n\n    workflow.start();\n\n    // Delayed executions are still created right away by the service but\n    // they have a firstWorkflowTaskBackoff set to the delay duration\n    // meaning the first workflow task is dispatched by service\n    // 2 second after exec is started in our sample\n    WorkflowExecutionHistory history = client.fetchHistory(WORKFLOW_ID);\n    com.google.protobuf.Duration backoff =\n        history\n            .getHistory()\n            .getEvents(0)\n            .getWorkflowExecutionStartedEventAttributes()\n            .getFirstWorkflowTaskBackoff();\n    System.out.println(\"First workflow task backoff: \" + backoff.getSeconds());\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloDetachedCancellationScope.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityCancellationType;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\n\n/**\n * This sample Temporal Workflow Definition demonstrates how to run \"cleanup\" code when a Workflow\n * Execution has been explicitly cancelled.\n */\npublic class HelloDetachedCancellationScope {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloDetachedCancellationTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloDetachedCancellationWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n\n    /** Query method to get the greeting */\n    @QueryMethod\n    String queryGreeting();\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    String sayHello(String name);\n\n    String sayGoodBye(String name);\n  }\n\n  /** This is the Greeting Activity Definition. */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String sayHello(String name) {\n      // This simulates a long-running Activity Execution so we can cancel the Workflow Execution\n      // before it completes.\n      for (int i = 0; i < Integer.MAX_VALUE; i++) {\n        try {\n          Thread.sleep(200);\n        } catch (InterruptedException e) {\n          // Wrap and re-throw the exception.\n          throw Activity.wrap(e);\n        }\n        Activity.getExecutionContext().heartbeat(i);\n      }\n      return \"unreachable\";\n    }\n\n    @Override\n    public String sayGoodBye(String name) {\n      return \"Goodbye \" + name + \"!\";\n    }\n  }\n\n  /** This is the Workflow Definition which implements our getGreeting method. */\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n    private String greeting;\n\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(10))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                .setHeartbeatTimeout(Duration.ofSeconds(2))\n                .build());\n\n    @Override\n    public String getGreeting(String name) {\n      try {\n        this.greeting = activities.sayHello(name);\n        return greeting;\n      } catch (ActivityFailure af) {\n        // Create a CancellationScope that is not linked to a parent scope\n        // This can be used in the \"cleanup\" code after the Workflow Execution has been cancelled.\n        CancellationScope detached =\n            Workflow.newDetachedCancellationScope(() -> greeting = activities.sayGoodBye(name));\n        detached.run();\n        throw af;\n      }\n    }\n\n    @Override\n    public String queryGreeting() {\n      return greeting;\n    }\n  }\n\n  public static void main(String[] args) throws InterruptedException {\n\n    // Get a Workflow service stub.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    /*\n     * Get a Workflow service client which can be used to start, Signal, and Query Workflow\n     * Executions.\n     */\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our Workflow Types with the Worker. Workflow Types must be known to the Worker at\n     * runtime.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the Workers that are in this process. The Workers will then start polling for\n     * Workflow Tasks and Activity Tasks.\n     */\n    factory.start();\n\n    // Create the Workflow client stub in order to start our Workflow Execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    WorkflowClient.start(workflow::getGreeting, \"John\");\n\n    WorkflowStub workflowStub = WorkflowStub.fromTyped(workflow);\n\n    Thread.sleep(1000);\n\n    // Cancel the Workflow Execution\n    // Note that this can be done from a different client.\n    workflowStub.cancel();\n\n    String result;\n\n    try {\n      // Wait for Workflow Execution results\n      // Because we cancelled the Workflow Execution we should get WorkflowFailedException\n      result = workflowStub.getResult(6, TimeUnit.SECONDS, String.class, String.class);\n    } catch (TimeoutException | WorkflowFailedException e) {\n      // Query the Workflow Execution to get the result which was set by the detached cancellation\n      // scope run\n      result = workflowStub.query(\"queryGreeting\", String.class);\n    }\n\n    System.out.println(result);\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloDynamic.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.activity.DynamicActivity;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.common.converter.EncodedValues;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.ActivityStub;\nimport io.temporal.workflow.DynamicSignalHandler;\nimport io.temporal.workflow.DynamicWorkflow;\nimport io.temporal.workflow.Workflow;\nimport java.io.IOException;\nimport java.time.Duration;\n\npublic class HelloDynamic {\n  // Define the task queue name\n  public static final String TASK_QUEUE = \"HelloDynamicTaskQueue\";\n\n  // Define our workflow unique id\n  public static final String WORKFLOW_ID = \"HelloDynamicWorkflow\";\n\n  // Dynamic Workflow Implementation\n  public static class DynamicGreetingWorkflowImpl implements DynamicWorkflow {\n    private String name;\n\n    @Override\n    public Object execute(EncodedValues args) {\n      String greeting = args.get(0, String.class);\n      String type = Workflow.getInfo().getWorkflowType();\n\n      // Register dynamic signal handler\n      Workflow.registerListener(\n          (DynamicSignalHandler)\n              (signalName, encodedArgs) -> name = encodedArgs.get(0, String.class));\n\n      // Define activity options and get ActivityStub\n      ActivityStub activity =\n          Workflow.newUntypedActivityStub(\n              ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n      // Execute the dynamic Activity. Note that the provided Activity name is not\n      // explicitly registered with the Worker\n      String greetingResult = activity.execute(\"DynamicACT\", String.class, greeting, name, type);\n\n      // Return results\n      return greetingResult;\n    }\n  }\n\n  // Dynamic Activity implementation\n  public static class DynamicGreetingActivityImpl implements DynamicActivity {\n    @Override\n    public Object execute(EncodedValues args) {\n      String activityType = Activity.getExecutionContext().getInfo().getActivityType();\n      return activityType\n          + \": \"\n          + args.get(0, String.class)\n          + \" \"\n          + args.get(1, String.class)\n          + \" from: \"\n          + args.get(2, String.class);\n    }\n  }\n\n  /**\n   * With our dynamic Workflow and Activities defined, we can now start execution. The main method\n   * starts the worker and then the workflow.\n   */\n  public static void main(String[] arg) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our dynamic workflow implementation with the worker. Workflow implementations must\n     * be known to the worker at runtime in order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(DynamicGreetingWorkflowImpl.class);\n\n    /*\n     * Register our dynamic workflow activity implementation with the worker. Since workflow\n     * activities are stateless and thread-safe, we need to register a shared instance.\n     */\n    worker.registerActivitiesImplementations(new DynamicGreetingActivityImpl());\n\n    /*\n     * Start all the Workers that are in this process. The Workers will then start polling for\n     * Workflow Tasks and Activity Tasks.\n     */\n    factory.start();\n\n    /*\n     * Create the workflow stub Note that the Workflow type is not explicitly registered with the\n     * Worker\n     */\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(WORKFLOW_ID).build();\n    WorkflowStub workflow = client.newUntypedWorkflowStub(\"DynamicWF\", workflowOptions);\n\n    // Start workflow execution and signal right after Pass in the workflow args and signal args\n    workflow.signalWithStart(\"greetingSignal\", new Object[] {\"John\"}, new Object[] {\"Hello\"});\n\n    // Wait for workflow to finish getting the results\n    String result = workflow.getResult(String.class);\n\n    System.out.println(result);\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloEagerWorkflowStart.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Sample Temporal Workflow Definition that starts eagerly and executes a single Local Activity.\n * Important elements of eager starting are: the client starting the workflow and the worker\n * executing it need to be shared, worker options needs to have .setDisableEagerExecution(false)\n * set, the activity is recommended to be a local activity for best performance\n */\npublic class HelloEagerWorkflowStart {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloEagerWorkflowStartTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloEagerWorkflowStartWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    @ActivityMethod(name = \"greet\")\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * overall timeout that our workflow is willing to wait for activity to complete. For this\n     * example it is set to 2 seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /** Simple activity implementation, that concatenates two strings. */\n  static class GreetingLocalActivitiesImpl implements GreetingActivities {\n    private static final Logger log = LoggerFactory.getLogger(GreetingLocalActivitiesImpl.class);\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      log.info(\"Composing greeting...\");\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     * Here it's important that the worker shares the client for eager execution.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingLocalActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .setDisableEagerExecution(false) // set this to enable eager execution\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     *\n     * See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloException.java",
    "content": "package io.temporal.samples.hello;\n\nimport com.google.common.base.Throwables;\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Sample Temporal workflow that demonstrates exception propagation across workflow activities,\n * child workflow, parent workflow, and the workflow client.\n */\npublic class HelloException {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloExceptionTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloExceptionWorkflow\";\n\n  /**\n   * Define the parent workflow interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * Define the parent workflow method. This method is executed when the workflow is started. The\n     * workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * Define the child workflow interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingChild {\n\n    /**\n     * Define the child workflow method. This method is executed when the workflow is started. The\n     * workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the parent workflow implementation. It implements the getGreeting workflow method\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    @Override\n    public String getGreeting(String name) {\n\n      // Create the child workflow stub\n      GreetingChild child = Workflow.newChildWorkflowStub(GreetingChild.class);\n      // Execute the child workflow\n      return child.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  // Define the child workflow implementation. It implements the composeGreeting workflow method\n  public static class GreetingChildImpl implements GreetingChild {\n\n    /*\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the\"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofMinutes(1))\n                .setScheduleToStartTimeout(Duration.ofSeconds(5))\n                .setRetryOptions(\n                    RetryOptions.newBuilder()\n                        .setInitialInterval(Duration.ofSeconds(1))\n                        .setMaximumAttempts(2)\n                        .build())\n                .build());\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return activities.composeGreeting(greeting, name);\n    }\n  }\n\n  /*\n   * Implementation of the workflow activity interface. It overwrites the defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      try {\n        // here we simulate IOException being thrown inside the activity method\n        // in order to show how it propagates through the workflow execution\n        throw new IOException(greeting + \" \" + name + \"!\");\n      } catch (IOException e) {\n        /*\n         * Instead of adding the thrown exception to the activity method signature\n         * wrap it using Workflow.wrap before re-throwing it.\n         * The original checked exception will be unwrapped and attached as the cause to the\n         * {@link io.temporal.failure.ActivityFailure}\n         */\n        throw Activity.wrap(e);\n      }\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow parent and child implementations with the worker.\n     * Since workflows are stateful in nature, we need to register our workflow types.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class, GreetingChildImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    try {\n      // Execute our parent workflow. This will call the child workflow, which then calls the\n      // defined workflow activity. The workflow activity throws the exception.\n      workflow.getGreeting(\"World\");\n      throw new IllegalStateException(\"unreachable\");\n    } catch (WorkflowException e) {\n\n      /*\n       * This stack trace should help you better understand\n       * how exception propagation works with Temporal.\n       *\n       * Looking at the stack trace from bottom-up (to understand the propagation) we first have:\n       * 1) Caused by: io.temporal.failure.ApplicationFailure: message='Hello World!', type='java.io.IOException'\n       * this is the IOException thrown by our activity.\n       * 2) Caused by: io.temporal.failure.ActivityFailure - indicates the execution failure of our activity\n       * 3) Caused by: io.temporal.failure.ChildWorkflowFailure - indicates the failure of our child workflow execution\n       * 4) io.temporal.client.WorkflowFailedException - indicates the failure of our workflow execution\n       */\n      System.out.println(\"\\nStack Trace:\\n\" + Throwables.getStackTraceAsString(e));\n\n      /* Now let's see if our original IOException was indeed propagated all the way to our\n       * WorkflowException which we caught in our code.\n       * To do this let's print out its root cause:\n       */\n      Throwable cause = Throwables.getRootCause(e);\n      // here we should get our originally thrown IOException and the message \"Hello World\"\n      System.out.println(\"\\n Root cause: \" + cause.getMessage());\n    }\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloLocalActivity.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.LocalActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Hello World Temporal workflow that executes a single local activity. Requires a local instance\n * the Temporal service to be running.\n *\n * <p>Some of the Activities are very short lived and do not need the queuing semantic, flow\n * control, rate limiting and routing capabilities. For these Temporal supports so called local\n * Activity feature. Local Activities are executed in the same worker process as the Workflow that\n * invoked them. Consider using local Activities for functions that are:\n *\n * <ul>\n *   <li>no longer than a few seconds\n *   <li>do not require global rate limiting\n *   <li>do not require routing to specific workers or pools of workers\n *   <li>can be implemented in the same binary as the Workflow that invokes them\n * </ul>\n *\n * <p>The main benefit of local Activities is that they are much more efficient in utilizing\n * Temporal service resources and have much lower latency overhead comparing to the usual Activity\n * invocation.\n */\npublic class HelloLocalActivity {\n\n  static final String TASK_QUEUE = \"HelloLocalActivity\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /** Activity interface is just a POJO. */\n  @ActivityInterface\n  public interface GreetingActivities {\n    @ActivityMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  /** GreetingWorkflow implementation that calls GreetingsActivities#composeGreeting. */\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Activity stub implements activity interface and proxies calls to it to Temporal activity\n     * invocations. Because activities are reentrant, only a single stub can be used for multiple\n     * activity invocations.\n     */\n    private final GreetingActivities activities =\n        Workflow.newLocalActivityStub(\n            GreetingActivities.class,\n            LocalActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(2))\n                .build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  static class GreetingLocalActivityImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    // Workflows are stateful. So you need a type to create instances.\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n    // Activities are stateless and thread safe. So a shared instance is used.\n    worker.registerActivitiesImplementations(new GreetingLocalActivityImpl());\n    // Start listening to the workflow and activity task queues.\n    factory.start();\n\n    // Start a workflow execution. Usually this is done from another program.\n    // Uses task queue from the GreetingWorkflow @WorkflowMethod annotation.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class, WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build());\n    // Execute a workflow waiting for it to complete. See {@link\n    // io.temporal.samples.hello.HelloSignal}\n    // for an example of starting workflow without waiting synchronously for its result.\n    String greeting = workflow.getGreeting(\"World\");\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloParallelActivity.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.List;\n\n/** Sample Temporal workflow that executes multiple Activity methods in parallel. */\npublic class HelloParallelActivity {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloParallelActivityTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloParallelActivityWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface MultiGreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    List<String> getGreetings(List<String> names);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    String composeGreeting(String greeting, String name);\n  }\n\n  /** Simple activity implementation, that concatenates two strings. */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class MultiGreetingWorkflowImpl implements MultiGreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * overall timeout that our workflow is willing to wait for activity to complete. For this\n     * example it is set to 2 seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public List<String> getGreetings(List<String> names) {\n      List<String> results = new ArrayList();\n\n      List<Promise<String>> promiseList = new ArrayList<>();\n      if (names != null) {\n        for (String name : names) {\n          promiseList.add(Async.function(activities::composeGreeting, \"Hello\", name));\n        }\n\n        // Invoke all activities in parallel. Wait for all to complete\n        Promise.allOf(promiseList).get();\n\n        // Loop through promises and get results\n        for (Promise<String> promise : promiseList) {\n          if (promise.getFailure() == null) {\n            results.add(promise.get());\n          }\n        }\n      }\n      return results;\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(MultiGreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    MultiGreetingWorkflow workflow =\n        client.newWorkflowStub(\n            MultiGreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreetings method is\n     * synchronous.\n     *\n     */\n    List<String> results =\n        workflow.getGreetings(Arrays.asList(\"John\", \"Mary\", \"Michael\", \"Jennet\"));\n\n    // Display workflow execution results\n    for (String result : results) {\n      System.out.println(result);\n    }\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloPeriodic.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowExecutionAlreadyStarted;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.Random;\n\n/**\n * Sample Temporal workflow that demonstrates periodic workflow execution with a random delay. To\n * learn about periodic execution with a fixed delay (defined by a cron), check out the {@link\n * HelloCron} example.\n */\npublic class HelloPeriodic {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloPeriodicTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloPeriodicWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    void greetPeriodically(String name);\n\n    /** Users will invoke this signal when they want the main workflow loop to complete. */\n    @SignalMethod\n    void requestExit();\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long-running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    void greet(String greeting);\n  }\n\n  // Define the workflow implementation which implements the greetPeriodically workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    // In this example, we use an internal of 5 seconds with an intended variation of +/- 1 second\n    // between executions of some useful work. In real applications a higher value may be more\n    // appropriate, for example one that matches a business cycle of several hours or even days.\n    private static final int SCHEDULE_PERIOD_TARGET_SECS = 5;\n    private static final int SCHEDULE_PERIOD_VARIATION_SECS = 2;\n\n    // The max history length of a single Temporal workflow is 50,000 commands.\n    // Therefore, a workflow cannot run indefinitely. Instead, we use\n    // the ContinueAsNew feature to continue the execution as a new run\n    // (same approach is used by Temporal's Cron-style scheduling system as well).\n    // In real life, the complexity of the workflow affects when we need to flow\n    // to a new run: I.e. if the workflow uses more Temporal commands per\n    // iteration, then the history grows faster, and thus we can perform fewer\n    // iterations before we must ContinueAsNew. For a simple workflow such as\n    // this, we could perform many thousands of iterations. However, for\n    // demonstration purposes we will flow to a new run more frequently.\n    // More details: https://docs.temporal.io/docs/java/workflows/#large-event-histories\n    private static final int SINGLE_WORKFLOW_ITERATIONS = 10;\n\n    // Here we introduce a random delay between periodic executions.\n    // Note that inside of workflow code 'Workflow.newRandom()' must always\n    // be used to construct instances of the 'Random' class.\n    private final Random random = Workflow.newRandom();\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside the workflow thread on the activity worker, that can be on a different\n     * host. Temporal is going to dispatch the activity results back to the workflow and unblock the\n     * stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    private boolean exitRequested = false;\n\n    @Override\n    public void requestExit() {\n      exitRequested = true;\n    }\n\n    @Override\n    public void greetPeriodically(String name) {\n\n      for (int i = 0; i < SINGLE_WORKFLOW_ITERATIONS; i++) {\n\n        // Compute the timing of the next iteration:\n        int delayMillis =\n            (SCHEDULE_PERIOD_TARGET_SECS * 1000)\n                + random.nextInt(SCHEDULE_PERIOD_VARIATION_SECS * 1000)\n                - (SCHEDULE_PERIOD_VARIATION_SECS * 500);\n\n        // Perform some useful work. In this example, we execute a greeting activity:\n        activities.greet(\n            \"Hello \"\n                + name\n                + \"!\"\n                + \" I will sleep for \"\n                + delayMillis\n                + \" milliseconds and then I will greet you again.\");\n\n        // Sleep for the required time period or until an exit signal is received:\n        Workflow.await(Duration.ofMillis(delayMillis), () -> exitRequested);\n\n        if (exitRequested) {\n          activities.greet(\n              \"Hello \"\n                  + name\n                  + \"!\"\n                  + \" It was requested to quit the periodic greetings, so this the last one.\");\n          return;\n        }\n      }\n\n      // Create a workflow stub that will be used to continue this workflow as a new run:\n      // (see the comment for 'SingleWorkflowRunIterations' for details)\n      GreetingWorkflow continueAsNew = Workflow.newContinueAsNewStub(GreetingWorkflow.class);\n\n      // Request that the new run will be invoked by the Temporal system:\n      continueAsNew.greetPeriodically(name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined greet activity\n   * method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public void greet(String greeting) {\n      System.out.println(\n          \"From \" + Activity.getExecutionContext().getInfo().getWorkflowId() + \": \" + greeting);\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  @SuppressWarnings(\n      \"CatchAndPrintStackTrace\") // in this simple example advanced error logging is not required\n  public static void main(String[] args) throws InterruptedException {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            // At most one instance.\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Execute our workflow.\n    try {\n      WorkflowClient.start(workflow::greetPeriodically, \"World\");\n      System.out.println(\"Started a new GreetingWorkflow.\");\n    } catch (WorkflowExecutionAlreadyStarted e) {\n      // If the workflow is already running, we cannot start it. Instead, we will connect to the\n      // existing instance.\n      workflow = client.newWorkflowStub(GreetingWorkflow.class, WORKFLOW_ID);\n      System.out.println(\"Connected to an existing GreetingWorkflow.\");\n    }\n\n    // The workflow is running now. In this example, we pause for a few seconds to observe its\n    // output.\n    final int ObservationPeriodSecs = 20;\n    System.out.println(\n        \"Observing the workflow execution for \" + ObservationPeriodSecs + \" seconds.\");\n\n    try {\n      Thread.sleep(ObservationPeriodSecs * 1000);\n    } catch (InterruptedException intEx) {\n      intEx.printStackTrace();\n    }\n\n    // Signal the workflow to exit.\n    System.out.println(\"Requesting the workflow to exit.\");\n    workflow.requestExit();\n\n    // Allow the workflow to finish before the worker is shut down.\n    WorkflowStub.fromTyped(workflow).getResult(void.class);\n\n    System.out.println(\"Good bye.\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloPolymorphicActivity.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Sample Temporal Workflow Definition that demonstrates the execution of multiple Activities which\n * extend a common interface.\n */\npublic class HelloPolymorphicActivity {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloPolymorphicActivityTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloPolymorphicActivityWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  // Define the base interface for the two workflow activities\n  // Note it is not annotated with @ActivityInterface\n  public interface GreetingActivity {\n    String composeGreeting(String name);\n  }\n\n  /**\n   * Define the first activity interface. Workflow methods can call activities during execution.\n   * Annotating activity methods with @ActivityMethod is optional\n   *\n   * <p>Note the activity interface extends the base GreetingActivity interface. Also note that in\n   * order to void the collisions in the activity name (which is by default the name of the activity\n   * method) we set the namePrefix annotation parameter.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface(namePrefix = \"Hello_\")\n  public interface HelloActivity extends GreetingActivity {\n    @Override\n    String composeGreeting(String name);\n  }\n\n  /**\n   * Define the second activity interface. Workflow methods can call activities during execution.\n   * Annotating activity methods with @ActivityMethod is optional\n   *\n   * <p>Note that the activity interface extends the base GreetingActivity interface. Also note that\n   * in order to void the collisions in the activity name (which is by default the name of the\n   * activity method) we set the namePrefix annotation parameter.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface(namePrefix = \"Bye_\")\n  public interface ByeActivity extends GreetingActivity {\n    @Override\n    String composeGreeting(String name);\n  }\n\n  // Define the workflow implementation which implements the getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>For this example we define two activity stubs, one for each of the defined activities.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    private final GreetingActivity[] activities =\n        new GreetingActivity[] {\n          Workflow.newActivityStub(\n              HelloActivity.class,\n              ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build()),\n          Workflow.newActivityStub(\n              ByeActivity.class,\n              ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build())\n        };\n\n    @Override\n    public String getGreeting(String name) {\n      StringBuilder result = new StringBuilder();\n\n      /*\n       * Call the composeGreeting activity method\n       * for each of our two activities.\n       * Notice how you can use the common activities interface for each.\n       *\n       * Append the result of each of the activity invocation results and return it.\n       */\n      for (GreetingActivity activity : activities) {\n        result.append(activity.composeGreeting(name));\n        result.append('\\n');\n      }\n      return result.toString();\n    }\n  }\n\n  // Hello workflow activity implementation\n  static class HelloActivityImpl implements HelloActivity {\n    @Override\n    public String composeGreeting(String name) {\n      return \"Hello \" + name + \"!\";\n    }\n  }\n\n  // Bye workflow activity implementation\n  static class ByeActivityImpl implements ByeActivity {\n    @Override\n    public String composeGreeting(String name) {\n      return \"Bye \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     Register our workflow activities implementations with the worker. Since workflow activities are\n     stateless and thread-safe, we need to register a shared instance.\n    */\n    worker.registerActivitiesImplementations(new HelloActivityImpl(), new ByeActivityImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our getGreeting method is\n     * synchronous.\n     *\n     * See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String greeting = workflow.getGreeting(\"World\");\n\n    // Print the workflow results. It should contain the results\n    // of both of our defined activities\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloQuery.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample Temporal Workflow Definition that demonstrates how to Query a Workflow. */\npublic class HelloQuery {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloQueryTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloQueryWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    @WorkflowMethod\n    void createGreeting(String name);\n\n    // Workflow query method. Used to return our greeting as a query value\n    @QueryMethod\n    String queryGreeting();\n  }\n\n  // Define the workflow implementation which implements our createGreeting and\n  // queryGreeting workflow methods.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    private String greeting;\n\n    @Override\n    public void createGreeting(String name) {\n      // We set the value of greeting to \"Hello\" first.\n      greeting = \"Hello \" + name + \"!\";\n      /*\n       * Note that our createGreeting workflow method has return type of void.\n       * It only sets the greeting and does not return it.\n       *\n       * Also note that inside a workflow method you should always\n       * use Workflow.sleep or Workflow.currentTimeMillis rather than the\n       * equivalent standard Java ones.\n       */\n      Workflow.sleep(Duration.ofSeconds(2));\n\n      // after two seconds we change the value of our greeting to \"Bye\"\n      greeting = \"Bye \" + name + \"!\";\n    }\n\n    // our workflow query method returns the greeting\n    @Override\n    public String queryGreeting() {\n      return greeting;\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws InterruptedException {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start our workflow asynchronously to not use another thread to query.\n    WorkflowClient.start(workflow::createGreeting, \"World\");\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send a signal to it using workflow stub.\n\n    /*\n     * Query our workflow to get the current value of greeting.\n     * Remember that original the workflow methods sets this value to \"Hello\"\n     * So here we should get \"Hello World\".\n     */\n    System.out.println(workflow.queryGreeting());\n\n    /*\n     * Sleep for 2.5 seconds. This value is set because remember in our\n     * workflow method the value of the greeting is updated after 2 seconds.\n     *\n     * Also note since here we are not inside a Workflow method, we can use the\n     * standard Java Thread.sleep.\n     */\n    Thread.sleep(2500);\n\n    /*\n     * Query our workflow to get the current value of greeting.\n     *\n     * Now we should get \"Bye World\".\n     */\n    System.out.println(workflow.queryGreeting());\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSaga.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Async;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Saga;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Sample Temporal workflow that demonstrates the workflow compensation capability.\n *\n * <p>Compensation deals with undoing or reversing work which has already successfully completed.\n * (also called SAGA). Temporal includes very powerful support for compensation which is showedcased\n * in this example.\n *\n * @see io.temporal.samples.bookingsaga.TripBookingSaga for another SAGA example.\n */\npublic class HelloSaga {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloSagaTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloSagaTaskWorkflow\";\n\n  /**\n   * Define the child workflow interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface ChildWorkflowOperation {\n\n    /**\n     * Define the child workflow method. This method is executed when the child workflow is started.\n     * The child workflow completes when the workflow method finishes execution.\n     */\n    @WorkflowMethod\n    void execute(int amount);\n  }\n\n  // Define the child workflow implementation. It implements the execute workflow method\n  public static class ChildWorkflowOperationImpl implements ChildWorkflowOperation {\n\n    /*\n     * Define the ActivityOperation stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    ActivityOperation activity =\n        Workflow.newActivityStub(\n            ActivityOperation.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public void execute(int amount) {\n      activity.execute(amount);\n    }\n  }\n\n  /**\n   * Define the child workflow compensation interface. It must contain one method annotated\n   * with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface ChildWorkflowCompensation {\n\n    /**\n     * Define the child workflow compensation method. This method is executed when the child\n     * workflow is started. The child workflow completes when the workflow method finishes\n     * execution.\n     */\n    @WorkflowMethod\n    void compensate(int amount);\n  }\n\n  // Define the child workflow compensation implementation. It implements the compensate child\n  // workflow method\n  public static class ChildWorkflowCompensationImpl implements ChildWorkflowCompensation {\n\n    /*\n     * Define the ActivityOperation stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the\"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    ActivityOperation activity =\n        Workflow.newActivityStub(\n            ActivityOperation.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public void compensate(int amount) {\n      activity.compensate(amount);\n    }\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface ActivityOperation {\n    @ActivityMethod\n    void execute(int amount);\n\n    @ActivityMethod\n    void compensate(int amount);\n  }\n\n  /**\n   * Implementation of the workflow activity interface. It overwrites the defined execute and\n   * compensate activity methods.\n   */\n  public static class ActivityOperationImpl implements ActivityOperation {\n\n    @Override\n    public void execute(int amount) {\n      System.out.println(\"ActivityOperationImpl.execute() is called with amount \" + amount);\n    }\n\n    @Override\n    public void compensate(int amount) {\n      System.out.println(\"ActivityCompensationImpl.compensate() is called with amount \" + amount);\n    }\n  }\n\n  /**\n   * Define the main workflow interface. It must contain one method annotated with @WorkflowMethod\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface SagaWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    void execute();\n  }\n\n  // Define the main workflow implementation. It implements the execute workflow method\n  public static class SagaWorkflowImpl implements SagaWorkflow {\n\n    /*\n     * Define the ActivityOperation stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    ActivityOperation activity =\n        Workflow.newActivityStub(\n            ActivityOperation.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public void execute() {\n\n      // {@link io.temporal.workflow.Saga} implements the logic to perform compensation operations\n      Saga saga = new Saga(new Saga.Options.Builder().setParallelCompensation(false).build());\n\n      try {\n\n        /*\n         * First we show how to compensate sync child workflow invocations. We first create a child\n         * workflow stub and execute its \"execute\" method. Then we create a stub of the child\n         * compensation workflow and register it with Saga. At this point this compensation workflow\n         * is not invoked. It is invoked explicitly when we actually want to invoke compensation\n         * (via saga.compensate()).\n         */\n        ChildWorkflowOperation op1 = Workflow.newChildWorkflowStub(ChildWorkflowOperation.class);\n        op1.execute(10);\n        ChildWorkflowCompensation c1 =\n            Workflow.newChildWorkflowStub(ChildWorkflowCompensation.class);\n        saga.addCompensation(c1::compensate, -10);\n\n        /*\n         * Now we show compensation of workflow activities which are invoked asynchronously. We\n         * invoke the activity \"execute\" method async. Then we register its \"compensate\" method as\n         * the compensation method for it.\n         *\n         * <p>Again note that the compensation of this activity again is only explicitly invoked\n         * (via saga.compensate()).\n         */\n        Promise<Void> result = Async.procedure(activity::execute, 20);\n        saga.addCompensation(activity::compensate, -20);\n        // get the result of the activity (blocking)\n        result.get();\n\n        /*\n         * You can also supply an arbitrary lambda expression as a saga\n         * compensation function.\n         * Note that this compensation function is not associated with a child workflow\n         * method or an activity method. It is associated with the currently executing\n         * workflow method.\n         *\n         * Also note that here in this example we use System.out in the main workflow logic.\n         * In production make sure to use Workflow.getLogger to log messages from workflow code.\n         */\n        saga.addCompensation(\n            () -> System.out.println(\"Other compensation logic in main workflow.\"));\n\n        /*\n         * Here we throw a runtime exception on purpose to showcase\n         * how to trigger compensation in case of an exception.\n         * Note that compensation can be also triggered\n         * without a specific exception being thrown. You can built in\n         * compensation to be part of your core workflow business requirements,\n         * meaning it can be triggered as part of your business logic.\n         */\n        throw new RuntimeException(\"some error\");\n\n      } catch (Exception e) {\n        // we catch our exception and trigger workflow compensation\n        saga.compensate();\n      }\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementations with the worker. Since workflows are stateful in nature,\n     * we need to register our workflow types.\n     */\n    worker.registerWorkflowImplementationTypes(\n        HelloSaga.SagaWorkflowImpl.class,\n        HelloSaga.ChildWorkflowOperationImpl.class,\n        HelloSaga.ChildWorkflowCompensationImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new ActivityOperationImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create our workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    HelloSaga.SagaWorkflow workflow =\n        client.newWorkflowStub(HelloSaga.SagaWorkflow.class, workflowOptions);\n\n    // Execute our workflow\n    workflow.execute();\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSchedules.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.api.enums.v1.ScheduleOverlapPolicy;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.schedules.*;\nimport io.temporal.common.converter.GlobalDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.time.Instant;\nimport java.util.Collections;\n\n/**\n * Sample Temporal workflow that demonstrates periodic workflow execution using a schedule. Schedule\n * is a new feature in Temporal designed to replace Cron workflows. Schedules allow for greater\n * control over when workflows are run and how they are run.\n */\npublic class HelloSchedules {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloScheduleTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloScheduleWorkflow\";\n\n  // Define the schedule unique id\n  static final String SCHEDULE_ID = \"HelloSchedule\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see WorkflowInterface\n   * @see WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    void greet(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called during workflow execution\n    void greet(String greeting);\n  }\n\n  // Define the workflow implementation which implements the greet workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs are proxies for activity invocations that\n     * are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 10\n     * seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n    @Override\n    public void greet(String name) {\n      // Workflow Executions started by a Schedule have the following\n      // additional properties appended to their search attributes.\n      Payload scheduledByIDPayload =\n          Workflow.getInfo().getSearchAttributes().getIndexedFieldsOrThrow(\"TemporalScheduledById\");\n      String scheduledByID =\n          GlobalDataConverter.get().fromPayload(scheduledByIDPayload, String.class, String.class);\n\n      Payload startTimePayload =\n          Workflow.getInfo()\n              .getSearchAttributes()\n              .getIndexedFieldsOrThrow(\"TemporalScheduledStartTime\");\n      Instant startTime =\n          GlobalDataConverter.get().fromPayload(startTimePayload, Instant.class, Instant.class);\n\n      activities.greet(\n          \"Hello \" + name + \" from \" + scheduledByID + \" scheduled at \" + startTime + \"!\");\n    }\n  }\n\n  /**\n   * Implementation of the workflow activity interface. It overwrites the defined greet activity\n   * method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public void greet(String greeting) {\n      System.out.println(\n          \"From \" + Activity.getExecutionContext().getInfo().getWorkflowId() + \": \" + greeting);\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws InterruptedException {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register the workflow activity implementation with the worker. Since workflow activities are\n     * stateless and thread-safe, we need to register a shared instance.\n     */\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    /*\n     * Get a Schedule client which can be used to interact with schedule.\n     */\n    ScheduleClient scheduleClient = ScheduleClient.newInstance(service);\n\n    /*\n     * Create the workflow options for our schedule.\n     * Note: Not all workflow options are supported for schedules.\n     */\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setWorkflowId(WORKFLOW_ID).setTaskQueue(TASK_QUEUE).build();\n\n    /*\n     * Create the action that will be run when the schedule is triggered.\n     */\n    ScheduleActionStartWorkflow action =\n        ScheduleActionStartWorkflow.newBuilder()\n            .setWorkflowType(HelloSchedules.GreetingWorkflow.class)\n            .setArguments(\"World\")\n            .setOptions(workflowOptions)\n            .build();\n\n    // Define the schedule we want to create\n    Schedule schedule =\n        Schedule.newBuilder().setAction(action).setSpec(ScheduleSpec.newBuilder().build()).build();\n\n    // Create a schedule on the server\n    ScheduleHandle handle =\n        scheduleClient.createSchedule(SCHEDULE_ID, schedule, ScheduleOptions.newBuilder().build());\n\n    // Manually trigger the schedule once\n    handle.trigger(ScheduleOverlapPolicy.SCHEDULE_OVERLAP_POLICY_ALLOW_ALL);\n\n    // Update the schedule with a spec, so it will run periodically\n    handle.update(\n        (ScheduleUpdateInput input) -> {\n          Schedule.Builder builder = Schedule.newBuilder(input.getDescription().getSchedule());\n\n          builder.setSpec(\n              ScheduleSpec.newBuilder()\n                  // Run the schedule at 5pm on Friday\n                  .setCalendars(\n                      Collections.singletonList(\n                          ScheduleCalendarSpec.newBuilder()\n                              .setHour(Collections.singletonList(new ScheduleRange(17)))\n                              .setDayOfWeek(Collections.singletonList(new ScheduleRange(5)))\n                              .build()))\n                  // Run the schedule every 5s\n                  .setIntervals(\n                      Collections.singletonList(new ScheduleIntervalSpec(Duration.ofSeconds(5))))\n                  .build());\n          // Make the schedule paused to demonstrate how to unpause a schedule\n          builder.setState(\n              ScheduleState.newBuilder()\n                  .setPaused(true)\n                  .setLimitedAction(true)\n                  .setRemainingActions(10)\n                  .build());\n          return new ScheduleUpdate(builder.build());\n        });\n\n    // Unpause schedule\n    handle.unpause();\n\n    // Wait for the schedule to run 10 actions\n    while (true) {\n      ScheduleState state = handle.describe().getSchedule().getState();\n      if (state.getRemainingActions() == 0) {\n        break;\n      }\n      Thread.sleep(5000);\n    }\n    // Delete the schedule once the sample is done\n    handle.delete();\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSearchAttributes.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.api.common.v1.SearchAttributes;\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.DataConverter;\nimport io.temporal.common.converter.GlobalDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.time.ZoneId;\nimport java.time.ZonedDateTime;\nimport java.time.format.DateTimeFormatter;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Sample Temporal workflow that demonstrates setting up and retrieving workflow search attributes.\n */\npublic class HelloSearchAttributes {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloSearchAttributesTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloSearchAttributesWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    @ActivityMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs implement activity interfaces and proxy\n     * calls to it to Temporal activity invocations. Since Temporal activities are reentrant, a\n     * single activity stub can be used for multiple activity invocations.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      return greeting + \" \" + name + \"!\";\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(HelloSearchAttributes.GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new HelloSearchAttributes.GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Set our workflow options.\n    // Note that we set our search attributes here\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(WORKFLOW_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setSearchAttributes(generateSearchAttributes())\n            .build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    HelloSearchAttributes.GreetingWorkflow workflow =\n        client.newWorkflowStub(HelloSearchAttributes.GreetingWorkflow.class, workflowOptions);\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"SearchAttributes\");\n\n    // Get the workflow execution for the same id as our defined workflow\n    WorkflowExecution execution = WorkflowExecution.newBuilder().setWorkflowId(WORKFLOW_ID).build();\n\n    // Create the DescribeWorkflowExecutionRequest through which we can query our client for our\n    // search queries\n    DescribeWorkflowExecutionRequest request =\n        DescribeWorkflowExecutionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setExecution(execution)\n            .build();\n\n    try {\n      // Get the DescribeWorkflowExecutionResponse from our service\n      DescribeWorkflowExecutionResponse resp =\n          service.blockingStub().describeWorkflowExecution(request);\n\n      // get all search attributes\n      SearchAttributes searchAttributes = resp.getWorkflowExecutionInfo().getSearchAttributes();\n      // Get the specific value of a keyword from the payload.\n      // In this case it is the \"CustomKeywordField\" with the value of \"keys\"\n      // You can update the code to extract other defined search attribute as well\n      String keyword = getKeywordFromSearchAttribute(searchAttributes, \"CustomKeywordField\");\n      // Print the value of the \"CustomKeywordField\" field\n      System.out.printf(\"In workflow we get CustomKeywordField is: %s\\n\", keyword);\n    } catch (Exception e) {\n      System.out.println(e);\n    }\n\n    // Print the workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n\n  // Generate our example search option\n  private static Map<String, Object> generateSearchAttributes() {\n    Map<String, Object> searchAttributes = new HashMap<>();\n    searchAttributes.put(\n        \"CustomKeywordField\",\n        \"keys\"); // each field can also be array such as: String[] keys = {\"k1\", \"k2\"};\n    searchAttributes.put(\"CustomIntField\", 1);\n    searchAttributes.put(\"CustomDoubleField\", 0.1);\n    searchAttributes.put(\"CustomBoolField\", true);\n    searchAttributes.put(\"CustomDatetimeField\", generateDateTimeFieldValue());\n    searchAttributes.put(\n        \"CustomStringField\",\n        \"String field is for text. When query, it will be tokenized for partial match. StringTypeField cannot be used in Order By\");\n    return searchAttributes;\n  }\n\n  // CustomDatetimeField takes times encoded in the  RFC 3339 format.\n  private static String generateDateTimeFieldValue() {\n    DateTimeFormatter formatter = DateTimeFormatter.ofPattern(\"yyyy-MM-dd'T'HH:mm:ssXXX\");\n    return ZonedDateTime.now(ZoneId.systemDefault()).format(formatter);\n  }\n\n  // example for extracting a value from search attributes\n  static String getKeywordFromSearchAttribute(SearchAttributes searchAttributes, String key) {\n    Payload field = searchAttributes.getIndexedFieldsOrThrow(key);\n    DataConverter dataConverter = GlobalDataConverter.get();\n    return dataConverter.fromPayload(field, String.class, String.class);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSideEffect.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.security.SecureRandom;\nimport java.time.Duration;\nimport java.util.Random;\nimport java.util.UUID;\n\n/**\n * Sample Temporal workflow that shows use of workflow SideEffect.\n *\n * <p>Workflow methods must be deterministic. In order to execute non-deterministic code, such as\n * random number generation as shown in this example, you should use Workflow.SideEffect.\n * Workflow.SideEffect is typically used for very quick-running operations, where as Workflow\n * Activities or Local Activities, which can also execute non-deterministic code, are meant for more\n * expensive operations.\n *\n * <p>Note: you should not use SideEffect function to modify the workflow state. For that you should\n * only use the SideEffect's return value!\n */\npublic class HelloSideEffect {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloSideEffectTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloSideEffectTaskWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow code includes core processing logic. It shouldn't contain any heavyweight\n   * computations, non-deterministic code, network calls, database operations, etc. All those things\n   * should be handled by Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface SideEffectWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String execute();\n\n    @QueryMethod\n    String getResult();\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface SideEffectActivities {\n\n    // Define your activity methods which can be called during workflow execution\n    String sayHello(String greeting);\n\n    String sayGoodBye(String greeting);\n  }\n\n  // Define the workflow implementation which implements our execute workflow method.\n  public static class SideEffectWorkflowImpl implements SideEffectWorkflow {\n\n    /**\n     * Define the SideEffectActivities stub. Activity stubs are proxies for activity invocations\n     * that are executed outside of the workflow thread on the activity worker, that can be on a\n     * different host. Temporal is going to dispatch the activity results back to the workflow and\n     * unblock the stub as soon as activity is completed on the activity worker.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets maximum\n     * time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    private final SideEffectActivities activities =\n        Workflow.newActivityStub(\n            SideEffectActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    int randomInt, sideEffectsRandomInt;\n    UUID randomUUID;\n    String result;\n\n    @Override\n    public String execute() {\n\n      // Replay-safe way to create random number using Workflow.newRandom\n      randomInt = Workflow.newRandom().nextInt();\n\n      // Replay-safe way to create random uuid\n      randomUUID = Workflow.randomUUID();\n\n      /*\n       * Random number using side effects. Note that this value is recorded in workflow history. On\n       * replay the same value is returned so determinism is guaranteed.\n       */\n      sideEffectsRandomInt =\n          Workflow.sideEffect(\n              int.class,\n              () -> {\n                Random random = new SecureRandom();\n                return random.nextInt();\n              });\n\n      /*\n       * Since our randoms are all created safely (using SideEffects or Workflow deterministic\n       * methods) the workflow result should be same as the queries ran after workflow completion.\n       * In the case we did not use safe methods, the queries could have a different result.\n       */\n      if ((randomUUID.version() + randomInt + sideEffectsRandomInt) % 2 == 0) {\n        result = activities.sayHello(\"World\");\n      } else {\n        result = activities.sayGoodBye(\"World!\");\n      }\n      return result;\n    }\n\n    @Override\n    public String getResult() {\n      return result;\n    }\n  }\n\n  /** Simple activity implementation. */\n  static class SideEffectActivitiesImpl implements SideEffectActivities {\n    @Override\n    public String sayHello(String greeting) {\n      return \"Hello \" + greeting;\n    }\n\n    @Override\n    public String sayGoodBye(String greeting) {\n      return \"Goodbye \" + greeting;\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker. Workflow implementations must be known\n     * to the worker at runtime in order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(SideEffectWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new SideEffectActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue. The started workers then start\n     * polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    SideEffectWorkflow workflow =\n        client.newWorkflowStub(\n            SideEffectWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our start method is\n     * synchronous.\n     *\n     * <p>See {@link io.temporal.samples.hello.HelloSignal} for an example of starting workflow\n     * without waiting synchronously for its result.\n     */\n    String result = workflow.execute();\n\n    // Print workflow result\n    System.out.println(result);\n\n    // Note that query should return the exact same result\n    System.out.println(workflow.getResult());\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSignal.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Sample Temporal workflow that demonstrates how to use workflow signal methods to signal from\n * external sources.\n */\npublic class HelloSignal {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloSignalTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloSignalWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see io.temporal.workflow.WorkflowInterface\n   * @see io.temporal.workflow.WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    List<String> getGreetings();\n\n    // Define the workflow waitForName signal method. This method is executed when the workflow\n    // receives a signal.\n    @SignalMethod\n    void waitForName(String name);\n\n    // Define the workflow exit signal method. This method is executed when the workflow receives a\n    // signal.\n    @SignalMethod\n    void exit();\n  }\n\n  // Define the workflow implementation which implements the getGreetings workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    // messageQueue holds up to 10 messages (received from signals)\n    List<String> messageQueue = new ArrayList<>(10);\n    boolean exit = false;\n\n    @Override\n    public List<String> getGreetings() {\n      List<String> receivedMessages = new ArrayList<>(10);\n\n      while (true) {\n        // Block current thread until the unblocking condition is evaluated to true\n        Workflow.await(() -> !messageQueue.isEmpty() || exit);\n        if (messageQueue.isEmpty() && exit) {\n          // no messages in queue and exit signal was sent, return the received messages\n          return receivedMessages;\n        }\n        String message = messageQueue.remove(0);\n        receivedMessages.add(message);\n      }\n    }\n\n    @Override\n    public void waitForName(String name) {\n      messageQueue.add(\"Hello \" + name + \"!\");\n    }\n\n    @Override\n    public void exit() {\n      exit = true;\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws Exception {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(WORKFLOW_ID).build();\n\n    // Create the workflow client stub. It is used to start the workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously and call its getGreeting workflow method\n    WorkflowClient.start(workflow::getGreetings);\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send a signal to it using the workflow stub.\n    // This workflow keeps receiving signals until exit is called\n\n    // When the workflow is started the getGreetings will block for the previously defined\n    // conditions\n    // Send the first workflow signal\n    workflow.waitForName(\"World\");\n\n    /*\n     * Here we create a new workflow stub using the same workflow id.\n     * We do this to demonstrate that to send a signal to an already running workflow\n     * you only need to know its workflow id.\n     */\n    GreetingWorkflow workflowById = client.newWorkflowStub(GreetingWorkflow.class, WORKFLOW_ID);\n\n    // Send the second signal to our workflow\n    workflowById.waitForName(\"Universe\");\n\n    // Now let's send our exit signal to the workflow\n    workflowById.exit();\n\n    /*\n     * We now call our getGreetings workflow method synchronously after our workflow has started.\n     * This reconnects our workflowById workflow stub to the existing workflow and blocks until\n     * a result is available. Note that this behavior assumes that WorkflowOptions are not configured\n     * with WorkflowIdReusePolicy.AllowDuplicate. If they were, this call would fail with the\n     * WorkflowExecutionAlreadyStartedException exception.\n     */\n    List<String> greetings = workflowById.getGreetings();\n\n    // Print our two greetings which were sent by signals\n    System.out.println(greetings);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSignalWithStartAndWorkflowInit.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.apache.commons.lang.StringUtils;\n\n/**\n * Sample Temporal workflow that demonstrates how to use WorkflowInit with clients starting\n * execution using SignalWithStart\n */\npublic class HelloSignalWithStartAndWorkflowInit {\n  static final String TASK_QUEUE = \"HelloWithInitTaskQueue\";\n\n  public interface MyWorkflow {\n    @WorkflowMethod\n    String greet(Person person);\n\n    @SignalMethod\n    void addGreeting(Person person);\n  }\n\n  @WorkflowInterface\n  public interface MyWorkflowWithInit extends MyWorkflow {}\n\n  @WorkflowInterface\n  public interface MyWorkflowNoInit extends MyWorkflow {}\n\n  public static class WithInitMyWorkflowImpl implements MyWorkflowWithInit {\n    // We dont initialize peopleToGreet on purpose\n    private List<Person> peopleToGreet;\n    private MyGreetingActivities activities =\n        Workflow.newActivityStub(\n            MyGreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @WorkflowInit\n    public WithInitMyWorkflowImpl(Person person) {\n      peopleToGreet = new ArrayList<>();\n    }\n\n    @Override\n    public String greet(Person person) {\n      peopleToGreet.add(person);\n      List<String> greetings = new ArrayList<>();\n\n      while (!peopleToGreet.isEmpty()) {\n        // run activity...\n        greetings.add(activities.greet(peopleToGreet.get(0)));\n        peopleToGreet.remove(0);\n      }\n      return StringUtils.join(greetings, \",\");\n    }\n\n    @Override\n    public void addGreeting(Person person) {\n      peopleToGreet.add(person);\n    }\n  }\n\n  public static class WithoutInitMyWorkflowImpl implements MyWorkflowNoInit {\n    // We dont initialize peopleToGreet on purpose\n    private List<Person> peopleToGreet;\n    private MyGreetingActivities activities =\n        Workflow.newActivityStub(\n            MyGreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String greet(Person person) {\n      peopleToGreet.add(person);\n      List<String> greetings = new ArrayList<>();\n\n      while (!peopleToGreet.isEmpty()) {\n        // run activity...\n        greetings.add(activities.greet(peopleToGreet.get(0)));\n        peopleToGreet.remove(0);\n      }\n      return StringUtils.join(greetings, \",\");\n    }\n\n    @Override\n    public void addGreeting(Person person) {\n      peopleToGreet.add(person);\n    }\n  }\n\n  @ActivityInterface\n  public interface MyGreetingActivities {\n    public String greet(Person person);\n  }\n\n  public static class MyGreetingActivitiesImpl implements MyGreetingActivities {\n    @Override\n    public String greet(Person person) {\n      return \"Hello \" + person.firstName + \" \" + person.lastName;\n    }\n  }\n\n  public static class Person {\n    String firstName;\n    String lastName;\n    int age;\n\n    public Person() {}\n\n    public Person(String firstName, String lastName, int age) {\n      this.firstName = firstName;\n      this.lastName = lastName;\n      this.age = age;\n    }\n\n    public String getFirstName() {\n      return firstName;\n    }\n\n    public void setFirstName(String firstName) {\n      this.firstName = firstName;\n    }\n\n    public String getLastName() {\n      return lastName;\n    }\n\n    public void setLastName(String lastName) {\n      this.lastName = lastName;\n    }\n\n    public int getAge() {\n      return age;\n    }\n\n    public void setAge(int age) {\n      this.age = age;\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(WithInitMyWorkflowImpl.class);\n    // We explicitly want to fail this workflow on NPE as thats what we expect without WorkflowInit\n    // As we didnt initialize peopleToGreet on purpose\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setFailWorkflowExceptionTypes(NullPointerException.class)\n            .build(),\n        WithoutInitMyWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new MyGreetingActivitiesImpl());\n\n    factory.start();\n\n    MyWorkflowWithInit withInitStub =\n        client.newWorkflowStub(\n            MyWorkflowWithInit.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"with-init\")\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    // Start with init workflow which is expected to succeed\n    // As WorkflowInit will initialize peopleToGreet before signal handler is invoked\n    WorkflowStub.fromTyped(withInitStub)\n        .signalWithStart(\n            \"addGreeting\",\n            new Object[] {new Person(\"Michael\", \"Jordan\", 55)},\n            new Object[] {new Person(\"John\", \"Stockton\", 57)});\n\n    String result = WorkflowStub.fromTyped(withInitStub).getResult(String.class);\n    System.out.println(\"Result: \" + result);\n\n    // Start without init, this execution is expected to fail as we set\n    // NullPointerException as a workflow failure type\n    // NPE is caused because we did not initialize peopleToGreet array\n    MyWorkflowNoInit noInitStub =\n        client.newWorkflowStub(\n            MyWorkflowNoInit.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"without-init\")\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    WorkflowStub.fromTyped(noInitStub)\n        .signalWithStart(\n            \"addGreeting\",\n            new Object[] {new Person(\"Michael\", \"Jordan\", 55)},\n            new Object[] {new Person(\"John\", \"Stockton\", 57)});\n    try {\n      WorkflowStub.fromTyped(noInitStub).getResult(String.class);\n    } catch (WorkflowFailedException e) {\n      System.out.println(\"Expected workflow failure: \" + e.getMessage());\n    }\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloSignalWithTimer.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\nimport org.slf4j.Logger;\n\n/**\n * Sample Temporal workflow that shows receiving signals for a specific time period and then process\n * last one received and continue as new.\n */\npublic class HelloSignalWithTimer {\n  static final String TASK_QUEUE = \"HelloSignalWithTimerTaskQueue\";\n  static final String WORKFLOW_ID = \"HelloSignalWithTimerWorkflow\";\n\n  @WorkflowInterface\n  public interface SignalWithTimerWorkflow {\n    @WorkflowMethod\n    void execute();\n\n    @SignalMethod\n    void newValue(String value);\n\n    @SignalMethod\n    void exit();\n  }\n\n  @ActivityInterface\n  public interface ValueProcessingActivities {\n    void processValue(String value);\n  }\n\n  public static class SignalWithTimerWorkflowImpl implements SignalWithTimerWorkflow {\n\n    private Logger logger = Workflow.getLogger(SignalWithTimerWorkflowImpl.class);\n    private String lastValue = \"\";\n    private CancellationScope timerScope;\n    private boolean exit = false;\n\n    private final ValueProcessingActivities activities =\n        Workflow.newActivityStub(\n            ValueProcessingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public void execute() {\n      // Just in case if exit signal is sent as soon as execution is started\n      if (exit) {\n        return;\n      }\n      // Start timer in cancellation scope so we can cancel it on exit signal received\n      timerScope =\n          Workflow.newCancellationScope(\n              () -> {\n                try {\n                  // You can add a signal handler that updates the sleep duration\n                  // As it may change via business logic over time\n                  // For sample we just hard code it to 5 seconds\n                  Workflow.newTimer(Duration.ofSeconds(5)).get();\n                } catch (CanceledFailure e) {\n                  // Exit signal is received causing cancellation of timer scope and timer\n                  // For sample we just log it, you can handle it if needed\n                  logger.info(\"Timer canceled via exit signal\");\n                }\n              });\n      timerScope.run();\n\n      // Process last received signal and either exit or ContinueAsNew depending on if we got\n      // Exit signal or not\n      activities.processValue(lastValue);\n\n      if (exit) {\n        return;\n      } else {\n        SignalWithTimerWorkflow nextRun =\n            Workflow.newContinueAsNewStub(SignalWithTimerWorkflow.class);\n        nextRun.execute();\n      }\n    }\n\n    @Override\n    public void newValue(String value) {\n      // Note that we can receive a signal at the same time workflow is trying to complete or\n      // ContinueAsNew. This would cause workflow task failure with UnhandledCommand\n      // in order to deliver this signal to our execution.\n      // You can choose what to do in this case depending on business logic.\n      // For this sample we just ignore it, alternative could be to process it or carry it over\n      // to the continued execution if needed.\n      lastValue = value;\n    }\n\n    @Override\n    public void exit() {\n      if (timerScope != null) {\n        timerScope.cancel(\"exit received\");\n      }\n      this.exit = true;\n    }\n  }\n\n  static class ValueProcessingActivitiesImpl implements ValueProcessingActivities {\n    @Override\n    public void processValue(String value) {\n      // Here you would access downstream services to process the value\n      // Dummy impl for sample, do nothing\n      System.out.println(\"Processing value: \" + value);\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(SignalWithTimerWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new ValueProcessingActivitiesImpl());\n\n    factory.start();\n\n    SignalWithTimerWorkflow workflow =\n        client.newWorkflowStub(\n            SignalWithTimerWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    // Start execution, this unblocks when its created by service\n    WorkflowClient.start(workflow::execute);\n\n    // Send signals 2s apart 12 times (to simulate cancellation on last ContinueAsNew)\n    for (int i = 0; i < 12; i++) {\n      workflow.newValue(\"Value \" + i);\n      sleep(2);\n    }\n    sleep(1);\n    // Send exit signal\n    workflow.exit();\n\n    // Wait for execution to complete after receiving exit signal.\n    // This should unblock pretty much immediately\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n\n    System.exit(0);\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(seconds * 1000L);\n    } catch (Exception e) {\n      System.out.println(\"Error: \" + e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloStandaloneActivity.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.client.ActivityClient;\nimport io.temporal.client.ActivityClientOptions;\nimport io.temporal.client.StartActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.time.Duration;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Sample Temporal application that executes a Standalone Activity — an Activity that runs\n * independently, without being orchestrated by a Workflow. Requires a local instance of the\n * Temporal service to be running.\n *\n * <p>Unlike regular Activities, a Standalone Activity is started directly from a Temporal Client\n * using {@link ActivityClient}, not from inside a Workflow Definition. Writing the Activity and\n * registering it with the Worker is identical in both cases.\n */\npublic class HelloStandaloneActivity {\n\n  static final String TASK_QUEUE = \"HelloStandaloneActivityTaskQueue\";\n  static final String ACTIVITY_ID = \"hello-standalone-activity-id\";\n\n  /**\n   * Activity interface. Writing a Standalone Activity is identical to writing an Activity\n   * orchestrated by a Workflow — the same Activity can be used for both.\n   *\n   * @see io.temporal.activity.ActivityInterface\n   * @see io.temporal.activity.ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n\n    // Define your activity method which can be called directly from a Temporal Client.\n    @ActivityMethod\n    String composeGreeting(String greeting, String name);\n  }\n\n  /** Simple activity implementation that concatenates two strings. */\n  public static class GreetingActivitiesImpl implements GreetingActivities {\n\n    private static final Logger log = LoggerFactory.getLogger(GreetingActivitiesImpl.class);\n\n    @Override\n    public String composeGreeting(String greeting, String name) {\n      log.info(\"Composing greeting...\");\n      return greeting + \", \" + name + \"!\";\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files.\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    // WorkflowClient is required to create a Worker.\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Worker factory that can be used to create workers for specific task queues.\n    WorkerFactory factory = WorkerFactory.newInstance(workflowClient);\n\n    // Worker that listens on a task queue and hosts activity implementations.\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    // Activities are stateless and thread safe. So a shared instance is used.\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    // Start listening to the activity task queue.\n    factory.start();\n\n    // ActivityClient executes standalone activities directly from application code,\n    // without a Workflow.\n    ActivityClient client =\n        ActivityClient.newInstance(\n            service,\n            ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());\n\n    // Options specifying the activity ID, task queue, and timeout.\n    StartActivityOptions options =\n        StartActivityOptions.newBuilder()\n            .setId(ACTIVITY_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setStartToCloseTimeout(Duration.ofSeconds(10))\n            .build();\n\n    try {\n      // Execute the activity and wait for its result. The typed API uses an unbound method\n      // reference so the SDK can infer the activity type name and result type automatically.\n      String result =\n          client.execute(\n              GreetingActivities.class,\n              GreetingActivities::composeGreeting,\n              options,\n              \"Hello\",\n              \"World\");\n\n      System.out.println(result);\n    } finally {\n      // Shut down the worker before the service so polling threads stop cleanly.\n      factory.shutdown();\n      service.shutdown();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloTypedSearchAttributes.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.SearchAttributeKey;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.time.OffsetDateTime;\nimport java.time.ZoneOffset;\nimport java.util.Arrays;\nimport java.util.List;\nimport java.util.StringJoiner;\n\n/**\n * Sample Temporal workflow that demonstrates setting up, updating, and retrieving workflow search\n * attributes using the typed search attributes API.\n *\n * <p>NOTE: you may need to add these custom search attributes yourself before running the sample.\n * If you are using autosetup image for service, you will need to create the\n * \"CustomKeywordListField\" search attribute with Temporal cli, for example:\n *\n * <p>temporal operator search-attribute create -name \"CustomKeywordListField\" -type \"KeywordList\"\n *\n * <p>If you run your test and don't have some custom SA defined that are used here you would see\n * error like: INVALID_ARGUMENT: Namespace default has no mapping defined for search attribute\n * CustomBoolField when trying to start the workflow execution. In that case use cli to add the\n * needed search attribute with its needed type.\n */\npublic class HelloTypedSearchAttributes {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloTypedSearchAttributesTaskQueue\";\n\n  // Define our workflow unique id\n  static final String WORKFLOW_ID = \"HelloTypedSearchAttributesWorkflow\";\n\n  // Define all our search attributes with appropriate types\n  static final SearchAttributeKey<String> CUSTOM_KEYWORD_SA =\n      SearchAttributeKey.forKeyword(\"CustomKeywordField\");\n  static final SearchAttributeKey<List<String>> CUSTOM_KEYWORD_LIST_SA =\n      SearchAttributeKey.forKeywordList(\"CustomKeywordListField\");\n  static final SearchAttributeKey<Long> CUSTOM_LONG_SA =\n      SearchAttributeKey.forLong(\"CustomIntField\");\n  static final SearchAttributeKey<Double> CUSTOM_DOUBLE_SA =\n      SearchAttributeKey.forDouble(\"CustomDoubleField\");\n  static final SearchAttributeKey<Boolean> CUSTOM_BOOL_SA =\n      SearchAttributeKey.forBoolean(\"CustomBoolField\");\n  static final SearchAttributeKey<OffsetDateTime> CUSTOM_OFFSET_DATE_TIME_SA =\n      SearchAttributeKey.forOffsetDateTime(\"CustomDatetimeField\");\n  static final SearchAttributeKey<String> CUSTOM_STRING_SA =\n      SearchAttributeKey.forText(\"CustomStringField\");\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see WorkflowInterface\n   * @see WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    String getGreeting(String name);\n  }\n\n  /**\n   * This is the Activity Definition's Interface. Activities are building blocks of any Temporal\n   * Workflow and contain any business logic that could perform long running computation, network\n   * calls, etc.\n   *\n   * <p>Annotating Activity Definition methods with @ActivityMethod is optional.\n   *\n   * @see ActivityInterface\n   * @see ActivityMethod\n   */\n  @ActivityInterface\n  public interface GreetingActivities {\n    @ActivityMethod\n    String composeGreeting(String greeting, List<String> salutations, String name);\n  }\n\n  // Define the workflow implementation which implements our getGreeting workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    /**\n     * Define the GreetingActivities stub. Activity stubs implement activity interfaces and proxy\n     * calls to it to Temporal activity invocations. Since Temporal activities are reentrant, a\n     * single activity stub can be used for multiple activity invocations.\n     *\n     * <p>In the {@link ActivityOptions} definition the \"setStartToCloseTimeout\" option sets the\n     * maximum time of a single Activity execution attempt. For this example it is set to 2 seconds.\n     */\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      // Show how to update typed search attributes inside a workflow. The first parameter shows how\n      // to remove a search attribute. The second parameter shows how to update a value.\n      Workflow.upsertTypedSearchAttributes(\n          CUSTOM_LONG_SA.valueUnset(), CUSTOM_KEYWORD_SA.valueSet(\"Hello\"));\n      // Get the search attributes currently set on this workflow\n      io.temporal.common.SearchAttributes searchAttributes = Workflow.getTypedSearchAttributes();\n      // Get a particular value out of the container using the typed key\n      String greeting = searchAttributes.get(CUSTOM_KEYWORD_SA);\n      List<String> salutations = searchAttributes.get(CUSTOM_KEYWORD_LIST_SA);\n      // This is a blocking call that returns only after the activity has completed.\n      return activities.composeGreeting(greeting, salutations, name);\n    }\n  }\n\n  /**\n   * Implementation of our workflow activity interface. It overwrites our defined composeGreeting\n   * activity method.\n   */\n  static class GreetingActivitiesImpl implements GreetingActivities {\n    @Override\n    public String composeGreeting(String greeting, List<String> salutations, String name) {\n      StringJoiner greetingJoiner = new StringJoiner(\" \");\n      greetingJoiner.add(greeting);\n      greetingJoiner.add(name);\n      salutations.forEach(s -> greetingJoiner.add(s));\n\n      return greetingJoiner.toString();\n    }\n  }\n\n  /**\n   * With our Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Define the workflow service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(\n        HelloTypedSearchAttributes.GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(\n        new HelloTypedSearchAttributes.GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Set our workflow options.\n    // Note that we set our search attributes here\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(WORKFLOW_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setTypedSearchAttributes(generateTypedSearchAttributes())\n            .build();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    HelloTypedSearchAttributes.GreetingWorkflow workflow =\n        client.newWorkflowStub(HelloTypedSearchAttributes.GreetingWorkflow.class, workflowOptions);\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"TypedSearchAttributes\");\n\n    // Print the workflow execution results\n    System.out.println(greeting);\n    System.exit(0);\n  }\n\n  // Generate our example search option\n  private static io.temporal.common.SearchAttributes generateTypedSearchAttributes() {\n    return io.temporal.common.SearchAttributes.newBuilder()\n        .set(CUSTOM_KEYWORD_SA, \"keyword\")\n        .set(CUSTOM_KEYWORD_LIST_SA, Arrays.asList(\"how\", \"are\", \"you\", \"doing?\"))\n        .set(CUSTOM_LONG_SA, 1l)\n        .set(CUSTOM_DOUBLE_SA, 0.1)\n        .set(CUSTOM_BOOL_SA, true)\n        .set(CUSTOM_OFFSET_DATE_TIME_SA, OffsetDateTime.now(ZoneOffset.UTC))\n        .set(\n            CUSTOM_STRING_SA,\n            \"String field is for text. When query, it will be tokenized for partial match. StringTypeField cannot be used in Order By\")\n        .build();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloUpdate.java",
    "content": "package io.temporal.samples.hello;\n\nimport com.google.common.base.Throwables;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.client.WorkflowUpdateException;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.UpdateValidatorMethod;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Sample Temporal workflow that demonstrates how to use workflow update methods to update a\n * workflow execution from external sources. Workflow update is another way to interact with a\n * running workflow along with signals and queries. Workflow update combines aspects of signals and\n * queries. Like signals, workflow update can mutate workflow state. Like queries, workflow update\n * can return a value.\n *\n * <p>Note: Make sure to set {@code frontend.enableUpdateWorkflowExecution=true} in your Temporal\n * config to enabled update.\n */\npublic class HelloUpdate {\n\n  // Define the task queue name\n  static final String TASK_QUEUE = \"HelloUpdateTaskQueue\";\n\n  // Define the workflow unique id\n  static final String WORKFLOW_ID = \"HelloUpdateWorkflow\";\n\n  /**\n   * The Workflow Definition's Interface must contain one method annotated with @WorkflowMethod.\n   *\n   * <p>Workflow Definitions should not contain any heavyweight computations, non-deterministic\n   * code, network calls, database operations, etc. Those things should be handled by the\n   * Activities.\n   *\n   * @see WorkflowInterface\n   * @see WorkflowMethod\n   */\n  @WorkflowInterface\n  public interface GreetingWorkflow {\n    /**\n     * This is the method that is executed when the Workflow Execution is started. The Workflow\n     * Execution completes when this method finishes execution.\n     */\n    @WorkflowMethod\n    List<String> getGreetings();\n\n    /*\n     * Define the workflow addGreeting update method. This method is executed when the workflow\n     * receives an update request.\n     */\n    @UpdateMethod\n    int addGreeting(String name);\n\n    /*\n     * Define an optional workflow update validator. The validator must take the same parameters as the update handle.\n     * The validator is run before the update handle.\n     * If the validator fails by throwing any exception the update request will be rejected and the handle will not run.\n     * If the validator passes the update will be considered accepted and the handler will run.\n     */\n    @UpdateValidatorMethod(updateName = \"addGreeting\")\n    void addGreetingValidator(String name);\n\n    // Define the workflow exit signal method. This method is executed when the workflow receives a\n    // signal.\n    @SignalMethod\n    void exit();\n  }\n\n  // Define the workflow implementation which implements the getGreetings workflow method.\n  public static class GreetingWorkflowImpl implements GreetingWorkflow {\n\n    // messageQueue holds up to 10 messages (received from updates)\n    private final List<String> messageQueue = new ArrayList<>(10);\n    private final List<String> receivedMessages = new ArrayList<>(10);\n    private boolean exit = false;\n\n    private final HelloActivity.GreetingActivities activities =\n        Workflow.newActivityStub(\n            HelloActivity.GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public List<String> getGreetings() {\n\n      while (true) {\n        // Block current thread until the unblocking condition is evaluated to true\n        Workflow.await(() -> !messageQueue.isEmpty() || exit);\n        if (messageQueue.isEmpty() && exit) {\n          /*\n           * no messages in queue and exit signal was sent, return the received messages.\n           *\n           * Note: A accepted update will not stop workflow completion. If a workflow tries to complete after an update\n           * has been sent by a client, but before it has been accepted by the workflow, the workflow will not complete.\n           */\n          return receivedMessages;\n        }\n        String message = messageQueue.remove(0);\n        receivedMessages.add(message);\n      }\n    }\n\n    @Override\n    public int addGreeting(String name) {\n      if (name.isEmpty()) {\n        /*\n         * Updates can fail by throwing a TemporalFailure. All other exceptions cause the workflow\n         * task to fail and potentially retried.\n         *\n         * Note: A check like this could (and should) belong in the validator, this is just to demonstrate failing an\n         * update.\n         */\n        throw ApplicationFailure.newFailure(\"Cannot greet someone with an empty name\", \"Failure\");\n      }\n      // Updates can mutate workflow state like variables or call activities\n      messageQueue.add(activities.composeGreeting(\"Hello\", name));\n      // Updates can return data back to the client\n      return receivedMessages.size() + messageQueue.size();\n    }\n\n    @Override\n    public void addGreetingValidator(String name) {\n      /*\n       * Update validators have the same restrictions as Queries. So workflow state cannot be\n       * mutated inside a validator.\n       */\n      if (receivedMessages.size() >= 10) {\n        /*\n         * Throwing any exception inside an update validator will cause the update to be rejected.\n         * Note: rejected update will not appear in the workflow history\n         */\n        throw new IllegalStateException(\"Only 10 greetings may be added\");\n      }\n    }\n\n    @Override\n    public void exit() {\n      exit = true;\n    }\n  }\n\n  /**\n   * With the Workflow and Activities defined, we can now start execution. The main method starts\n   * the worker and then the workflow.\n   */\n  public static void main(String[] args) throws Exception {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Get a Workflow service stub.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    /*\n     * Define the workflow factory. It is used to create workflow workers for a specific task queue.\n     */\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register the workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    worker.registerActivitiesImplementations(new HelloActivity.GreetingActivitiesImpl());\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).setWorkflowId(WORKFLOW_ID).build();\n\n    // Create the workflow client stub. It is used to start the workflow execution.\n    GreetingWorkflow workflow = client.newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously and call its getGreeting workflow method\n    WorkflowClient.start(workflow::getGreetings);\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send an update to it using the workflow stub.\n    // This workflow keeps receiving updates until exit is called\n\n    // When the workflow is started the getGreetings will block for the previously defined\n    // conditions\n    // Send the first workflow update\n    workflow.addGreeting(\"World\");\n\n    /*\n     * Here we create a new workflow stub using the same workflow id.\n     * We do this to demonstrate that to send an update to an already running workflow\n     * you only need to know its workflow id.\n     */\n    GreetingWorkflow workflowById = client.newWorkflowStub(GreetingWorkflow.class, WORKFLOW_ID);\n\n    // Send the second update to our workflow\n    workflowById.addGreeting(\"Universe\");\n\n    /*\n     * Create an untyped workflow stub to demonstrate sending an update\n     * with the untyped stub.\n     */\n    WorkflowStub greetingStub = client.newUntypedWorkflowStub(WORKFLOW_ID);\n    greetingStub.update(\"addGreeting\", int.class, \"Temporal\");\n\n    try {\n      // The update request will fail on a empty name and the exception will be thrown here.\n      workflowById.addGreeting(\"\");\n      System.exit(-1);\n    } catch (WorkflowUpdateException e) {\n      Throwable cause = Throwables.getRootCause(e);\n      /*\n       * Here we should get our originally thrown ApplicationError\n       * and the message \"Cannot greet someone with an empty name\".\n       */\n      System.out.println(\"\\n Update failed, root cause: \" + cause.getMessage());\n    }\n    // Send our update validators limit of 10 updates\n    int sentUpdates = workflowById.addGreeting(\"Update\");\n    while (sentUpdates < 10) {\n      sentUpdates = workflowById.addGreeting(\"Again\");\n    }\n\n    // The update request will be rejected because our validator will fail\n    try {\n      workflowById.addGreeting(\"Will be rejected\");\n      System.exit(-1);\n    } catch (WorkflowUpdateException e) {\n      Throwable cause = Throwables.getRootCause(e);\n      System.out.println(\"\\n Update rejected: \" + cause.getMessage());\n    }\n\n    // Now let's send our exit signal to the workflow\n    workflowById.exit();\n\n    /*\n     * We now call our getGreetings workflow method synchronously after our workflow has started.\n     * This reconnects our workflowById workflow stub to the existing workflow and blocks until\n     * a result is available. Note that this behavior assumes that WorkflowOptions are not configured\n     * with WorkflowIdReusePolicy.AllowDuplicate. If they were, this call would fail with the\n     * WorkflowExecutionAlreadyStartedException exception.\n     */\n    List<String> greetings = workflowById.getGreetings();\n\n    // Print our two greetings which were sent by signals\n    System.out.println(greetings);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/HelloWorkflowTimer.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.*;\nimport io.temporal.client.ActivityCompletionException;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.failure.ChildWorkflowFailure;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.workflow.*;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/** Sample shows how to use workflow timer instead of WorkflowOptions->Run/ExecutionTimeout */\npublic class HelloWorkflowTimer {\n  private static String WORKFLOW_ID = \"HelloWorkflowWithTimer\";\n  private static String TASK_QUEUE = \"HelloWorkflowWithTimerTaskQueue\";\n  // Change time to 12 to 20 seconds to handle cancellation while child workflow is running\n  private static int TIME_SECS = 8;\n\n  // Workflow\n  @WorkflowInterface\n  public interface WorkflowWithTimer {\n    @WorkflowMethod\n    String execute(String input);\n  }\n\n  public static class WorkflowWithTimerImpl implements WorkflowWithTimer {\n    // Our timer cancellation scope\n    private CancellationScope timerCancellationScope;\n    // Our workflow cancellation scope\n    private CancellationScope workflowCancellationScope;\n    // Workflow result\n    private String workflowResult = \"\";\n    private Promise<Void> workflowTimerPromise;\n\n    @Override\n    public String execute(String input) {\n      // Create workflow timer (within timer cancel;ation scope so it can be canceled)\n      // which denotes the max amount of time we allow this execution to run\n      // Using workflow timer instead of workflow run/execution timeouts allow us to react to this\n      // timer\n      // fires, be able to chose if we want to fail or complete execution, and do some \"cleanup\"\n      // tasks if\n      // necessary before we do so. If we used workflow run/execution timeouts insted we would not\n      // be able\n      // to react to this timer firing (its server timer only)\n      timerCancellationScope =\n          Workflow.newCancellationScope(\n              () -> {\n                workflowTimerPromise =\n                    Workflow.newTimer(\n                            Duration.ofSeconds(TIME_SECS),\n                            TimerOptions.newBuilder().setSummary(\"Workflow Timer\").build())\n                        // We can use thenApply here to cancel our cancelation scope when this timer\n                        // fires. Note we cannot complete the execution from here, see\n                        // https://github.com/temporalio/sdk-java/issues/87\n                        .thenApply(\n                            ignore -> {\n                              // Cancel the workflow cancellation scope allowing us to react to this\n                              // timer firing\n                              if (workflowCancellationScope != null) {\n                                workflowCancellationScope.cancel(\"Workflow timer fired\");\n                              }\n                              return null;\n                            });\n              });\n      timerCancellationScope.run();\n\n      // Create workflow cancellation scope in which we put our core business logic\n      workflowCancellationScope =\n          Workflow.newCancellationScope(\n              () -> {\n                WorkflowWithTimerActivities activities =\n                    Workflow.newActivityStub(\n                        WorkflowWithTimerActivities.class,\n                        ActivityOptions.newBuilder()\n                            .setStartToCloseTimeout(Duration.ofSeconds(12))\n                            // Set heartbeat timeout to 1s\n                            .setHeartbeatTimeout(Duration.ofSeconds(2))\n                            // We want to wait for activity to complete cancellation\n                            .setCancellationType(\n                                ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                            .build());\n\n                WorkflowWithTimerChildWorkflow childWorkflow =\n                    Workflow.newChildWorkflowStub(\n                        WorkflowWithTimerChildWorkflow.class,\n                        ChildWorkflowOptions.newBuilder()\n                            .setWorkflowId(WORKFLOW_ID + \"-Child\")\n                            // We want to wait for child workflow cancellation completion\n                            .setCancellationType(\n                                ChildWorkflowCancellationType.WAIT_CANCELLATION_COMPLETED)\n                            .build());\n\n                try {\n                  // Run our activities\n                  workflowResult = activities.sayHello(input);\n                  // Then our child workflow\n                  childWorkflow.executeChild(input);\n                } catch (ActivityFailure af) {\n                  // Handle cancellation of scope while activities are pending (running)\n                  if (af.getCause() instanceof CanceledFailure) {\n                    workflowResult = \"Workflow timer fired while activities were executing.\";\n                    // Here we can do more work if needed\n                  }\n                } catch (ChildWorkflowFailure cwf) {\n                  // Handle cancellation of scope while child workflow is pending (running)\n                  if (cwf.getCause() instanceof CanceledFailure) {\n                    workflowResult = \"Workflow timer fired while child workflow was executing.\";\n                    // Here we can do more work if needed\n                  }\n                }\n              });\n      // Run the workflow cancellation scope\n      // We need to handle CanceledFailure here in case we cancel the scope\n      // right before activity/child workflows are scheduled\n      try {\n        workflowCancellationScope.run();\n      } catch (CanceledFailure e) {\n        workflowResult = \"Workflow cancelled.\";\n      }\n\n      // Cancel our workflow timer if it didnt fire\n      if (!workflowTimerPromise.isCompleted()) {\n        timerCancellationScope.cancel(\"Workflow completed before workflow timer.\");\n      }\n\n      return workflowResult;\n    }\n  }\n\n  // Activities\n  @ActivityInterface\n  public interface WorkflowWithTimerActivities {\n    String sayHello(String input);\n  }\n\n  public static class WorkflowWithTimerActivitiesImpl implements WorkflowWithTimerActivities {\n    @Override\n    public String sayHello(String input) {\n      // here we just heartbeat then sleep for 1s\n      for (int i = 0; i < 10; i++) {\n        try {\n          Activity.getExecutionContext().heartbeat(\"heartbeating: \" + i);\n        } catch (ActivityCompletionException e) {\n          // Do some cleanup if needed, then re-throw\n          throw e;\n        }\n        sleep(1);\n      }\n      return \"Hello \" + input;\n    }\n\n    // Just sample sleep method\n    private void sleep(int seconds) {\n      try {\n        Thread.sleep(seconds * 1000L);\n      } catch (Exception e) {\n        System.out.println(e.getMessage());\n      }\n    }\n  }\n\n  // Child Workflows\n  @WorkflowInterface\n  public interface WorkflowWithTimerChildWorkflow {\n    @WorkflowMethod\n    String executeChild(String input);\n  }\n\n  public static class WorkflowWithTimerChildWorkflowImpl implements WorkflowWithTimerChildWorkflow {\n    @Override\n    public String executeChild(String input) {\n      // For sample we just sleep for 5 seconds and return some result\n      try {\n        Workflow.sleep(Duration.ofSeconds(5));\n        return \"From executeChild - \" + input;\n        // Note that similarly to parent workflow if child is running activities/child workflows\n        // we need to handle this in same way as parent does\n        // Fpr sample we can just handle CanceledFailure and rethrow\n      } catch (CanceledFailure e) {\n        // Can do cleanup if needed\n        throw e;\n      }\n    }\n  }\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Create service stubs\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // Create workflow client\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    // Create worker factory\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    // Create worker\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    // Register workflow and child workflow\n    worker.registerWorkflowImplementationTypes(\n        WorkflowWithTimerImpl.class, WorkflowWithTimerChildWorkflowImpl.class);\n    // Register activities\n    worker.registerActivitiesImplementations(new WorkflowWithTimerActivitiesImpl());\n\n    // Start factory (and worker)\n    factory.start();\n\n    // Create workflow stub\n    WorkflowWithTimer workflow =\n        client.newWorkflowStub(\n            WorkflowWithTimer.class,\n            WorkflowOptions.newBuilder()\n                // Note we do not set workflow run/execution timeouts\n                // As its not recommended in most cases\n                // In same we show how we can implement this with workflow timer instead\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Start workflow execution async\n    WorkflowClient.start(workflow::execute, \"Some Name Here\");\n\n    // Wait for execution to complete (sync)\n    WorkflowStub workflowStub = WorkflowStub.fromTyped(workflow);\n    String result = workflowStub.getResult(String.class);\n    System.out.println(\"Workflow result: \" + result);\n\n    // Stop main method\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/hello/README.md",
    "content": "## Hello samples\n\nEach Hello World sample  demonstrates one feature of the SDK in a single file.\n\n**Note that the single file format is used for sample brevity and is not something we recommend for real applications.**\n\nTo run each hello world sample, use one of the following commands:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloAccumulator\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloActivity\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloActivityRetry\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloActivityExclusiveChoice\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloAsync\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloParallelActivity\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloAsyncActivityCompletion\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloAsyncLambda\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloCancellationScope\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloDetachedCancellationScope\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloChild\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloCron\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloDynamic\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloEagerWorkflowStart\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloException\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloLocalActivity\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloPeriodic\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloPolymorphicActivity\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloQuery\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSaga\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSchedules\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSignal\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSearchAttributes\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloTypedSearchAttributes\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSideEffect\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloUpdate\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSignalWithTimer\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloSignalWithStartAndWorkflowInit\n./gradlew -q execute -PmainClass=io.temporal.samples.hello.HelloStandaloneActivity\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/keymanagementencryption/awsencryptionsdk/EncryptedPayloads.java",
    "content": "package io.temporal.samples.keymanagementencryption.awsencryptionsdk;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.CodecDataConverter;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.hello.HelloActivity;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.Collections;\nimport software.amazon.cryptography.materialproviders.IKeyring;\nimport software.amazon.cryptography.materialproviders.MaterialProviders;\nimport software.amazon.cryptography.materialproviders.model.CreateAwsKmsMultiKeyringInput;\nimport software.amazon.cryptography.materialproviders.model.MaterialProvidersConfig;\n\npublic class EncryptedPayloads {\n\n  static final String TASK_QUEUE = \"EncryptedPayloads\";\n\n  public static void main(String[] args) {\n    // Configure your keyring. In this sample we are configuring a basic AWS KMS keyring, but the\n    // AWS encryption SDK has multiple options depending on your use case.\n    //\n    // See more here:\n    // https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/which-keyring.html\n    String generatorKey = System.getenv(\"AWS_KEY_ARN\");\n\n    final MaterialProviders materialProviders =\n        MaterialProviders.builder()\n            .MaterialProvidersConfig(MaterialProvidersConfig.builder().build())\n            .build();\n    // Create the AWS KMS keyring\n    final CreateAwsKmsMultiKeyringInput keyringInput =\n        CreateAwsKmsMultiKeyringInput.builder().generator(generatorKey).build();\n    final IKeyring kmsKeyring = materialProviders.CreateAwsKmsMultiKeyring(keyringInput);\n    // gRPC stubs wrapper that talks to the local docker instance of temporal service.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder()\n                .setDataConverter(\n                    new CodecDataConverter(\n                        DefaultDataConverter.newDefaultInstance(),\n                        // Create our encryption codec\n                        Collections.singletonList(new KeyringCodec(kmsKeyring))))\n                .build());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    // Worker that listens on a task queue and hosts both workflow and activity implementations.\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    // Register the workflows and activities\n    worker.registerWorkflowImplementationTypes(HelloActivity.GreetingWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new HelloActivity.GreetingActivitiesImpl());\n    // Start listening to the workflow and activity task queues.\n    factory.start();\n\n    // Start a workflow execution.\n    HelloActivity.GreetingWorkflow workflow =\n        client.newWorkflowStub(\n            HelloActivity.GreetingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"My Secret Friend\");\n    System.out.println(greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/keymanagementencryption/awsencryptionsdk/KeyringCodec.java",
    "content": "package io.temporal.samples.keymanagementencryption.awsencryptionsdk;\n\nimport com.amazonaws.encryptionsdk.AwsCrypto;\nimport com.google.protobuf.ByteString;\nimport com.google.protobuf.InvalidProtocolBufferException;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.common.converter.EncodingKeys;\nimport io.temporal.payload.codec.PayloadCodec;\nimport io.temporal.payload.context.ActivitySerializationContext;\nimport io.temporal.payload.context.HasWorkflowSerializationContext;\nimport io.temporal.payload.context.SerializationContext;\nimport io.temporal.workflow.unsafe.WorkflowUnsafe;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Collections;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport javax.annotation.Nonnull;\nimport javax.annotation.Nullable;\nimport org.jetbrains.annotations.NotNull;\nimport software.amazon.cryptography.materialproviders.IKeyring;\n\n/**\n * KeyringCodec is a {@link PayloadCodec} that encrypts and decrypts payloads using the AWS\n * Encryption SDK. It uses the provided {@link IKeyring} to encrypt and decrypt payloads. It can\n * optionally support using a {@link SerializationContext}.\n */\nclass KeyringCodec implements PayloadCodec {\n  // Metadata encoding key for the AWS Encryption SDK\n  static final ByteString METADATA_ENCODING =\n      ByteString.copyFrom(\"awsencriptionsdk/binary/encrypted\", StandardCharsets.UTF_8);\n\n  private final AwsCrypto crypto;\n  private final IKeyring kmsKeyring;\n  private final boolean useSerializationContext;\n  @Nullable private final SerializationContext serializationContext;\n\n  /**\n   * Constructs a new KeyringCodec with the provided {@link IKeyring}. The codec will not use a\n   * {@link SerializationContext}.\n   *\n   * @param kmsKeyring the keyring to use for encryption and decryption.\n   */\n  public KeyringCodec(IKeyring kmsKeyring) {\n    this.crypto = AwsCrypto.standard();\n    this.kmsKeyring = kmsKeyring;\n    this.useSerializationContext = false;\n    this.serializationContext = null;\n  }\n\n  /**\n   * Constructs a new KeyringCodec with the provided {@link IKeyring}.\n   *\n   * @param crypto the AWS Crypto object to use for encryption and decryption.\n   * @param kmsKeyring the keyring to use for encryption and decryption.\n   * @param useSerializationContext whether to use a {@link SerializationContext} for encoding and\n   *     decoding payloads.\n   */\n  public KeyringCodec(AwsCrypto crypto, IKeyring kmsKeyring, boolean useSerializationContext) {\n    this.crypto = crypto;\n    this.kmsKeyring = kmsKeyring;\n    this.useSerializationContext = useSerializationContext;\n    this.serializationContext = null;\n  }\n\n  private KeyringCodec(\n      AwsCrypto crypto, IKeyring kmsKeyring, SerializationContext serializationContext) {\n    this.crypto = crypto;\n    this.kmsKeyring = kmsKeyring;\n    this.useSerializationContext = true;\n    this.serializationContext = serializationContext;\n  }\n\n  @NotNull\n  @Override\n  public List<Payload> encode(@NotNull List<Payload> payloads) {\n    // Disable deadlock detection for encoding payloads because this may make a network call\n    // to encrypt the data.\n    return WorkflowUnsafe.deadlockDetectorOff(\n        () -> payloads.stream().map(this::encodePayload).collect(Collectors.toList()));\n  }\n\n  @NotNull\n  @Override\n  public List<Payload> decode(@NotNull List<Payload> payloads) {\n    // Disable deadlock detection for decoding payloads because this may make a network call\n    // to decrypt the data.\n    return WorkflowUnsafe.deadlockDetectorOff(\n        () -> payloads.stream().map(this::decodePayload).collect(Collectors.toList()));\n  }\n\n  @NotNull\n  @Override\n  public PayloadCodec withContext(@Nonnull SerializationContext context) {\n    if (!useSerializationContext) {\n      return this;\n    }\n    return new KeyringCodec(crypto, kmsKeyring, context);\n  }\n\n  private Map<String, String> getEncryptionContext() {\n    // If we are not using a serialization context, return an empty map\n    // There may not be a serialization context if certain cases, such as when the codec is used\n    // for encoding/decoding payloads for a Nexus operation.\n    if (!useSerializationContext\n        || serializationContext == null\n        || !(serializationContext instanceof HasWorkflowSerializationContext)) {\n      return Collections.emptyMap();\n    }\n    String workflowId = ((HasWorkflowSerializationContext) serializationContext).getWorkflowId();\n    String activityType = null;\n    if (serializationContext instanceof ActivitySerializationContext) {\n      activityType = ((ActivitySerializationContext) serializationContext).getActivityType();\n    }\n    String signature = activityType != null ? workflowId + activityType : workflowId;\n    return Collections.singletonMap(\"signature\", signature);\n  }\n\n  private Payload encodePayload(Payload payload) {\n    byte[] plaintext = payload.toByteArray();\n    byte[] ciphertext =\n        crypto.encryptData(kmsKeyring, plaintext, getEncryptionContext()).getResult();\n    return Payload.newBuilder()\n        .setData(ByteString.copyFrom(ciphertext))\n        .putMetadata(EncodingKeys.METADATA_ENCODING_KEY, METADATA_ENCODING)\n        .build();\n  }\n\n  private Payload decodePayload(Payload payload) {\n    if (METADATA_ENCODING.equals(\n        payload.getMetadataOrDefault(EncodingKeys.METADATA_ENCODING_KEY, null))) {\n      byte[] ciphertext = payload.getData().toByteArray();\n      byte[] plaintext =\n          crypto.decryptData(kmsKeyring, ciphertext, getEncryptionContext()).getResult();\n      try {\n        return Payload.parseFrom(plaintext);\n      } catch (InvalidProtocolBufferException e) {\n        throw new RuntimeException(e);\n      }\n    }\n    return payload;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/keymanagementencryption/awsencryptionsdk/README.md",
    "content": "## AWS Encryption SDK Sample\n\nThis sample demonstrates how a user can leverage the [AWS Encryption](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/java.html) SDK to build a `PayloadCodec` to encrypt and decrypt payloads using [AWS KMS](https://aws.amazon.com/kms/) and envelope encryption.\n\n### About the AWS Encryption SDK:\n\n>The AWS Encryption SDK is a client-side encryption library designed to make it easy for everyone to encrypt and decrypt data using industry standards and best practices. It enables you to focus on the core functionality of your application, rather than on how to best encrypt and decrypt your data. The AWS Encryption SDK is provided free of charge under the Apache 2.0 license.\n\nFor more details please see [Amazons Documentation](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/introduction.html)\n\n### Choosing a Key Ring \n\nThis sample uses am [AWS KMS keyring](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-kms-keyring.html). This approach is convenient as you don't need to manage or secure your own keys. One drawback of this approach is it will require a call to KMS every time you need encrypt or decrypt data. If this is a concern you may want to consider using an [AWS KMS Hierarchical keyring](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/use-hierarchical-keyring.html).\n\nNote: You can also use the AWS Encryption SDK without any AWS services using the raw keyrings.\n\nFor more details please see [Amazons Documentation on choosing a key ring](https://docs.aws.amazon.com/encryption-sdk/latest/developer-guide/which-keyring.html).\n\n### Running this sample\n\nMake sure your AWS account credentials are up-to-date and can access KMS.\n\nExport the following environment variables\n-  `AWS_KEY_ARN`: Your AWS key ARN.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.keymanagementencryption.awsencryptionsdk.EncryptedPayloads\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/Customer.java",
    "content": "package io.temporal.samples.listworkflows;\n\npublic class Customer {\n  private String accountNum;\n  private String name;\n  private String email;\n  private String customerType;\n\n  public Customer() {}\n\n  public Customer(String accountNum, String name, String email, String customerType) {\n    this.accountNum = accountNum;\n    this.name = name;\n    this.email = email;\n    this.customerType = customerType;\n  }\n\n  public String getAccountNum() {\n    return accountNum;\n  }\n\n  public void setAccountNum(String accountNum) {\n    this.accountNum = accountNum;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getEmail() {\n    return email;\n  }\n\n  public void setEmail(String email) {\n    this.email = email;\n  }\n\n  public String getCustomerType() {\n    return customerType;\n  }\n\n  public void setCustomerType(String customerType) {\n    this.customerType = customerType;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/CustomerActivities.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface CustomerActivities {\n  void getCustomerAccount(Customer customer);\n\n  void updateCustomerAccount(Customer customer, String message);\n\n  void sendUpdateEmail(Customer customer);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/CustomerActivitiesImpl.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport java.util.concurrent.TimeUnit;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CustomerActivitiesImpl implements CustomerActivities {\n\n  private static final Logger log = LoggerFactory.getLogger(CustomerActivitiesImpl.class);\n\n  @Override\n  public void getCustomerAccount(Customer customer) {\n    // simulate some actual work...\n    sleepSeconds(1);\n  }\n\n  @Override\n  public void updateCustomerAccount(Customer customer, String message) {\n    // simulate some actual work...\n    sleepSeconds(1);\n  }\n\n  @Override\n  public void sendUpdateEmail(Customer customer) {\n    // simulate some actual work...\n    sleepSeconds(1);\n  }\n\n  private void sleepSeconds(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException e) {\n      // This is being swallowed on purpose\n      Thread.currentThread().interrupt();\n      log.error(\"Exception in thread sleep: \", e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/CustomerWorkflow.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CustomerWorkflow {\n  @WorkflowMethod\n  void updateAccountMessage(Customer customer, String message);\n\n  @SignalMethod\n  void exit();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/CustomerWorkflowImpl.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.Optional;\n\npublic class CustomerWorkflowImpl implements CustomerWorkflow {\n  private boolean exit;\n  private final CustomerActivities customerActivities =\n      Workflow.newActivityStub(\n          CustomerActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n  private final RetryOptions customerRetryOptions =\n      RetryOptions.newBuilder().setMaximumAttempts(5).build();\n  private final Duration expiration = Duration.ofMinutes(1);\n\n  @Override\n  public void updateAccountMessage(Customer customer, String message) {\n\n    Workflow.retry(\n        customerRetryOptions,\n        Optional.of(expiration),\n        () -> {\n          customerActivities.getCustomerAccount(customer);\n          customerActivities.updateCustomerAccount(customer, message);\n          customerActivities.sendUpdateEmail(customer);\n        });\n\n    Workflow.await(Duration.ofMinutes(1), () -> exit);\n  }\n\n  @Override\n  public void exit() {\n    this.exit = true;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/README.md",
    "content": "# Demo List Workflows\n\nThe sample demonstrates:\n1) Setting custom search attributes for a Workflow\n2) Using ListWorkflowExecutionsRequest and custom Search Attribute query to list\nWorkflow Executions that match that query\n\n## Running\n\n1. Unlike the other examples, this one has to be started with Elasticsearch \ncapabilities enabled. If you are using docker you can do that with:\n\n```bash\ngit clone https://github.com/temporalio/docker-compose.git\ncd  docker-compose\ndocker-compose -f docker-compose-cas-es.yml up\n```\n\n2. \nRun the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.listworkflows.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/listworkflows/Starter.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport io.temporal.api.enums.v1.WorkflowExecutionStatus;\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.ListWorkflowExecutionsRequest;\nimport io.temporal.api.workflowservice.v1.ListWorkflowExecutionsResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.concurrent.TimeUnit;\n\npublic class Starter {\n  public static final String TASK_QUEUE = \"customerTaskQueue\";\n  private static WorkflowServiceStubs service;\n  private static WorkflowClient client;\n  private static WorkerFactory factory;\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    factory = WorkerFactory.newInstance(client);\n\n    // create some fake customers\n    List<Customer> customers = new ArrayList<>();\n    customers.add(new Customer(\"c1\", \"John\", \"john@john.com\", \"new\"));\n    customers.add(new Customer(\"c2\", \"Mary\", \"mary@mary.com\", \"established\"));\n    customers.add(new Customer(\"c3\", \"Richard\", \"richard@richard.com\", \"established\"));\n    customers.add(new Customer(\"c4\", \"Anna\", \"anna@anna.com\", \"new\"));\n    customers.add(new Customer(\"c5\", \"Michael\", \"michael@michael.com\", \"established\"));\n\n    // create the worker for workflow and activities\n    createWorker();\n\n    // start customer workflows and define custom search attributes for each\n    startWorkflows(customers);\n\n    // small delay before we start querying executions\n    try {\n      Thread.sleep(2 * 1000);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(\"Exception happened in thread sleep: \", e);\n    }\n\n    // query \"new\" customers for all \"CustomerWorkflow\" workflows with status \"Running\" (1)\n    ListWorkflowExecutionsResponse newCustomersResponse =\n        getExecutionsResponse(\n            \"WorkflowType='CustomerWorkflow' and CustomStringField='new' and ExecutionStatus=\"\n                + WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_RUNNING_VALUE);\n\n    System.out.println(\"***** Customers with type 'new'\");\n    System.out.println(\n        \"Currently being processed: \" + newCustomersResponse.getExecutionsList().size());\n    List<WorkflowExecutionInfo> newExecutionInfo = newCustomersResponse.getExecutionsList();\n    for (WorkflowExecutionInfo wei : newExecutionInfo) {\n      System.out.println(\"Customer: \" + wei.getExecution().getWorkflowId());\n    }\n\n    // query \"established\" customers for all \"CustomerWorkflow\" workflows with status \"Running\" (1)\n    ListWorkflowExecutionsResponse establishedCustomersResponse =\n        getExecutionsResponse(\n            \"WorkflowType = 'CustomerWorkflow' and CustomStringField='established' and ExecutionStatus=\"\n                + WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_RUNNING_VALUE);\n\n    System.out.println(\"\\n***** Customers with type 'established'\");\n    System.out.println(\n        \"Currently being processed: \" + establishedCustomersResponse.getExecutionsList().size());\n    List<WorkflowExecutionInfo> establishedExecutionInfo =\n        establishedCustomersResponse.getExecutionsList();\n    for (WorkflowExecutionInfo wei : establishedExecutionInfo) {\n      System.out.println(\"Customer: \" + wei.getExecution().getWorkflowId());\n    }\n\n    // signal exit to all customer workflows\n    stopWorkflows(customers);\n\n    // sleep for 3 seconds before we shut down the worker\n    sleep(3);\n    System.exit(0);\n  }\n\n  private static ListWorkflowExecutionsResponse getExecutionsResponse(String query) {\n    ListWorkflowExecutionsRequest listWorkflowExecutionRequest =\n        ListWorkflowExecutionsRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setQuery(query)\n            .build();\n    ListWorkflowExecutionsResponse listWorkflowExecutionsResponse =\n        service.blockingStub().listWorkflowExecutions(listWorkflowExecutionRequest);\n    return listWorkflowExecutionsResponse;\n  }\n\n  private static void createWorker() {\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(CustomerWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new CustomerActivitiesImpl());\n\n    factory.start();\n  }\n\n  private static Map<String, Object> generateSearchAttributesFor(Customer customer) {\n    Map<String, Object> searchAttributes = new HashMap<>();\n    searchAttributes.put(\"CustomStringField\", customer.getCustomerType());\n\n    return searchAttributes;\n  }\n\n  private static void startWorkflows(List<Customer> customers) {\n    // start a workflow for each customer that we need to add message to account\n    for (Customer c : customers) {\n      String message = \"New message for: \" + c.getName();\n      WorkflowOptions newCustomerWorkflowOptions =\n          WorkflowOptions.newBuilder()\n              .setWorkflowId(c.getAccountNum())\n              .setTaskQueue(TASK_QUEUE)\n              // set the search attributes for this customer workflow\n              .setSearchAttributes(generateSearchAttributesFor(c))\n              .build();\n      CustomerWorkflow newCustomerWorkflow =\n          client.newWorkflowStub(CustomerWorkflow.class, newCustomerWorkflowOptions);\n      // start async\n      WorkflowClient.start(newCustomerWorkflow::updateAccountMessage, c, message);\n    }\n  }\n\n  private static void stopWorkflows(List<Customer> customers) {\n    for (Customer c : customers) {\n      CustomerWorkflow existingCustomerWorkflow =\n          client.newWorkflowStub(CustomerWorkflow.class, c.getAccountNum());\n      // signal the exist method to stop execution\n      existingCustomerWorkflow.exit();\n    }\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException e) {\n      System.out.println(\"Exception: \" + e.getMessage());\n      System.exit(0);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/MetricsStarter.java",
    "content": "package io.temporal.samples.metrics;\n\nimport com.sun.net.httpserver.HttpServer;\nimport com.uber.m3.tally.RootScopeBuilder;\nimport com.uber.m3.tally.Scope;\nimport com.uber.m3.util.ImmutableMap;\nimport io.micrometer.prometheus.PrometheusConfig;\nimport io.micrometer.prometheus.PrometheusMeterRegistry;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.reporter.MicrometerClientStatsReporter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.metrics.workflow.MetricsWorkflow;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport java.io.IOException;\n\npublic class MetricsStarter {\n  public static void main(String[] args) {\n    // Set up prometheus registry and stats reported\n    PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n    // Set up a new scope, report every 1 second\n    Scope scope =\n        new RootScopeBuilder()\n            // shows how to set custom tags\n            .tags(\n                ImmutableMap.of(\n                    \"starterCustomTag1\",\n                    \"starterCustomTag1Value\",\n                    \"starterCustomTag2\",\n                    \"starterCustomTag2Value\"))\n            .reporter(new MicrometerClientStatsReporter(registry))\n            .reportEvery(com.uber.m3.util.Duration.ofSeconds(1));\n    // Start the prometheus scrape endpoint for starter metrics\n    HttpServer scrapeEndpoint = MetricsUtils.startPrometheusScrapeEndpoint(registry, 8078);\n    // Stopping the starter will stop the http server that exposes the\n    // scrape endpoint.\n    Runtime.getRuntime().addShutdownHook(new Thread(() -> scrapeEndpoint.stop(1)));\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Add metrics scope to workflow service stub options, preserving env config\n    WorkflowServiceStubsOptions stubOptions =\n        WorkflowServiceStubsOptions.newBuilder(profile.toWorkflowServiceStubsOptions())\n            .setMetricsScope(scope)\n            .build();\n\n    WorkflowServiceStubs service = WorkflowServiceStubs.newServiceStubs(stubOptions);\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(\"metricsWorkflow\")\n            .setTaskQueue(MetricsWorker.DEFAULT_TASK_QUEUE_NAME)\n            .build();\n    MetricsWorkflow workflow = client.newWorkflowStub(MetricsWorkflow.class, workflowOptions);\n\n    String result = workflow.exec(\"hello metrics\");\n\n    System.out.println(\"Result: \" + result);\n\n    System.out.println(\"Starter metrics are available at http://localhost:8078/metrics\");\n\n    // We don't shut down the process here so metrics can be viewed.\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/MetricsUtils.java",
    "content": "package io.temporal.samples.metrics;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\n\nimport com.sun.net.httpserver.HttpServer;\nimport io.micrometer.prometheus.PrometheusMeterRegistry;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.net.InetSocketAddress;\n\npublic class MetricsUtils {\n\n  /**\n   * Starts HttpServer to expose a scrape endpoint. See\n   * https://micrometer.io/docs/registry/prometheus for more info.\n   */\n  public static HttpServer startPrometheusScrapeEndpoint(\n      PrometheusMeterRegistry registry, int port) {\n    try {\n      HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);\n      server.createContext(\n          \"/metrics\",\n          httpExchange -> {\n            String response = registry.scrape();\n            httpExchange.sendResponseHeaders(200, response.getBytes(UTF_8).length);\n            try (OutputStream os = httpExchange.getResponseBody()) {\n              os.write(response.getBytes(UTF_8));\n            }\n          });\n\n      server.start();\n      return server;\n    } catch (IOException e) {\n      throw new RuntimeException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/MetricsWorker.java",
    "content": "package io.temporal.samples.metrics;\n\nimport com.sun.net.httpserver.HttpServer;\nimport com.uber.m3.tally.RootScopeBuilder;\nimport com.uber.m3.tally.Scope;\nimport com.uber.m3.util.ImmutableMap;\nimport io.micrometer.prometheus.PrometheusConfig;\nimport io.micrometer.prometheus.PrometheusMeterRegistry;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.common.reporter.MicrometerClientStatsReporter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.metrics.activities.MetricsActivitiesImpl;\nimport io.temporal.samples.metrics.workflow.MetricsWorkflowImpl;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class MetricsWorker {\n\n  // task queue to be used for this sample\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"metricsqueue\";\n\n  public static void main(String[] args) {\n\n    // Set up prometheus registry and stats reported\n    PrometheusMeterRegistry registry = new PrometheusMeterRegistry(PrometheusConfig.DEFAULT);\n    // Set up a new scope, report every 1 second\n    Scope scope =\n        new RootScopeBuilder()\n            // shows how to set custom tags\n            .tags(\n                ImmutableMap.of(\n                    \"workerCustomTag1\",\n                    \"workerCustomTag1Value\",\n                    \"workerCustomTag2\",\n                    \"workerCustomTag2Value\"))\n            .reporter(new MicrometerClientStatsReporter(registry))\n            .reportEvery(com.uber.m3.util.Duration.ofSeconds(1));\n    // Start the prometheus scrape endpoint\n    HttpServer scrapeEndpoint = MetricsUtils.startPrometheusScrapeEndpoint(registry, 8077);\n    // Stopping the worker will stop the http server that exposes the\n    // scrape endpoint.\n    Runtime.getRuntime().addShutdownHook(new Thread(() -> scrapeEndpoint.stop(1)));\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // Add metrics scope to workflow service stub options, preserving env config\n    WorkflowServiceStubsOptions stubOptions =\n        WorkflowServiceStubsOptions.newBuilder(profile.toWorkflowServiceStubsOptions())\n            .setMetricsScope(scope)\n            .build();\n\n    WorkflowServiceStubs service = WorkflowServiceStubs.newServiceStubs(stubOptions);\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(MetricsWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new MetricsActivitiesImpl());\n\n    factory.start();\n\n    System.out.println(\"Workers metrics are available at http://localhost:8077/metrics\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/README.md",
    "content": "# Setting up SDK metrics (Prometheus)\n\nThis sample shows setup for SDK metrics.\n\n1. Start the Worker:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.metrics.MetricsWorker\n```\n\n2. Start the Starter:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.metrics.MetricsStarter\n```\n\n3. See the worker metrics on the exposed Prometheus Scrape Endpoint: [http://localhost:8077/metrics](http://localhost:8077/metrics)\n\n4. See the starter metrics on the exposed Prometheus Scrape Endpoint [http://localhost:8078/metrics](http://localhost:8078/metrics)\n\n5. Stop the worker and starter\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/activities/MetricsActivities.java",
    "content": "package io.temporal.samples.metrics.activities;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface MetricsActivities {\n  String performA(String input);\n\n  String performB(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/activities/MetricsActivitiesImpl.java",
    "content": "package io.temporal.samples.metrics.activities;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityExecutionContext;\n\npublic class MetricsActivitiesImpl implements MetricsActivities {\n\n  @Override\n  public String performA(String input) {\n    // simulate some failures to trigger retries\n    if (Activity.getExecutionContext().getInfo().getAttempt() < 3) {\n      incRetriesCustomMetric(Activity.getExecutionContext());\n      throw Activity.wrap(new NullPointerException(\"simulated\"));\n    }\n    return \"Performed activity A with input \" + input + \"\\n\";\n  }\n\n  @Override\n  public String performB(String input) {\n    // simulate some failures to trigger retries\n    if (Activity.getExecutionContext().getInfo().getAttempt() < 5) {\n      incRetriesCustomMetric(Activity.getExecutionContext());\n      throw Activity.wrap(new NullPointerException(\"simulated\"));\n    }\n    return \"Performed activity B with input \" + input + \"\\n\";\n  }\n\n  private void incRetriesCustomMetric(ActivityExecutionContext context) {\n    // We can create a child scope and add extra tags\n    //    Scope scope =\n    //        context\n    //            .getMetricsScope()\n    //            .tagged(\n    //                Stream.of(\n    //                        new String[][] {\n    //                          {\"workflow_id\", context.getInfo().getWorkflowId()},\n    //                          {\"activity_id\", context.getInfo().getActivityId()},\n    //                          {\n    //                            \"activity_start_to_close_timeout\",\n    //                            context.getInfo().getStartToCloseTimeout().toString()\n    //                          },\n    //                        })\n    //                    .collect(Collectors.toMap(data -> data[0], data -> data[1])));\n    //\n    //    scope.counter(\"custom_activity_retries\").inc(1);\n\n    // For sample we use root scope\n    context.getMetricsScope().counter(\"custom_activity_retries\").inc(1);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/workflow/MetricsWorkflow.java",
    "content": "package io.temporal.samples.metrics.workflow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MetricsWorkflow {\n  @WorkflowMethod\n  String exec(String input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/metrics/workflow/MetricsWorkflowImpl.java",
    "content": "package io.temporal.samples.metrics.workflow;\n\nimport com.uber.m3.tally.Scope;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.metrics.activities.MetricsActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.Collections;\n\npublic class MetricsWorkflowImpl implements MetricsWorkflow {\n\n  private final MetricsActivities activities =\n      Workflow.newActivityStub(\n          MetricsActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String exec(String input) {\n    /*\n     * Custom metric, we can use child scope and attach workflow_id as it's not attached by default\n     * like task_queue ,workflow_type, etc\n     */\n    Scope scope =\n        Workflow.getMetricsScope()\n            .tagged(Collections.singletonMap(\"workflow_id\", Workflow.getInfo().getWorkflowId()));\n    scope.counter(\"custom_metric\").inc(1);\n\n    String result = activities.performA(input);\n    Workflow.sleep(Duration.ofSeconds(5));\n    result += activities.performB(input);\n\n    return result;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/Account.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface Account {\n\n  void deposit(String accountId, String referenceId, int amountCents);\n\n  void withdraw(String accountId, String referenceId, int amountCents);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/AccountActivityWorker.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class AccountActivityWorker {\n\n  static final String TASK_QUEUE = \"Account\";\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    Account account = new AccountImpl();\n    worker.registerActivitiesImplementations(account);\n\n    factory.start();\n    System.out.println(\"Activity Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/AccountImpl.java",
    "content": "package io.temporal.samples.moneybatch;\n\npublic class AccountImpl implements Account {\n  @Override\n  public void deposit(String accountId, String referenceId, int amountCents) {\n    System.out.printf(\n        \"Deposit to %s of %d cents requested. ReferenceId=%s\\n\",\n        accountId, amountCents, referenceId);\n    //    throw new RuntimeException(\"simulated\"); // Uncomment to simulate failure\n  }\n\n  @Override\n  public void withdraw(String accountId, String referenceId, int amountCents) {\n    System.out.printf(\n        \"Withdraw to %s of %d cents requested. ReferenceId=%s\\n\",\n        accountId, amountCents, referenceId);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/AccountTransferWorker.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class AccountTransferWorker {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(AccountActivityWorker.TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(AccountTransferWorkflowImpl.class);\n\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + AccountActivityWorker.TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/AccountTransferWorkflow.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface AccountTransferWorkflow {\n\n  @WorkflowMethod\n  void deposit(String toAccountId, int batchSize);\n\n  @SignalMethod\n  void withdraw(String fromAccountId, String referenceId, int amountCents);\n\n  @QueryMethod\n  int getBalance();\n\n  @QueryMethod\n  int getCount();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/AccountTransferWorkflowImpl.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.HashSet;\nimport java.util.Set;\n\npublic class AccountTransferWorkflowImpl implements AccountTransferWorkflow {\n\n  private final ActivityOptions options =\n      ActivityOptions.newBuilder()\n          .setStartToCloseTimeout(Duration.ofSeconds(5))\n          .setRetryOptions(\n              RetryOptions.newBuilder()\n                  .setInitialInterval(Duration.ofSeconds(1))\n                  .setMaximumInterval(Duration.ofSeconds(10))\n                  .build())\n          .build();\n\n  private final Account account = Workflow.newActivityStub(Account.class, options);\n\n  private Set<String> references = new HashSet<>();\n  private int balance;\n  private int count;\n\n  @Override\n  public void deposit(String toAccount, int batchSize) {\n    Workflow.await(() -> count == batchSize);\n    String referenceId = Workflow.randomUUID().toString();\n    account.deposit(toAccount, referenceId, balance);\n  }\n\n  @Override\n  public void withdraw(String fromAccountId, String referenceId, int amountCents) {\n    if (!references.add(referenceId)) {\n      return; // duplicate\n    }\n    account.withdraw(fromAccountId, referenceId, amountCents);\n    balance += amountCents;\n    count++;\n  }\n\n  @Override\n  public int getBalance() {\n    return balance;\n  }\n\n  @Override\n  public int getCount() {\n    return count;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/README.md",
    "content": "# Demonstrates Signal Batching\n\nDemonstrates a situation when a single deposit should be initiated for multiple withdrawals.\nFor example, a seller might want to be paid once per fixed number of transactions.\nThe sample can be easily extended to perform a payment based on more complex criteria like a specific time or accumulated amount.\n\nThe sample also demonstrates the *signal with start* way of starting Workflows.\nIf the Workflow is already running, it just receives the Signal. If it is not running, then it is started first, and then the signal is delivered to it.\nYou can think about *signal with start* as a lazy way to create Workflows when signaling them.\n\n**How to run the Money Batch Sample**\n\nMoney Batch sample has three separate processes. One to host Workflow Executions,\nanother to host Activity Executions, and the third one to request transfers (start Workflow Executions).\n\nStart Workflow Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneybatch.AccountTransferWorker\n```\n\nStart Activity Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneybatch.AccountActivityWorker\n```\n\nExecute at least three times to request three transfers (example batch size):\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneybatch.TransferRequester\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneybatch/TransferRequester.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport io.temporal.client.BatchRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.util.Random;\nimport java.util.UUID;\n\npublic class TransferRequester {\n\n  /** Number of withdrawals to batch */\n  public static final int BATCH_SIZE = 3;\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    String reference = UUID.randomUUID().toString();\n    int amountCents = (new Random().nextInt(5) + 1) * 25;\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    String from = \"account1\";\n    String to = \"account2\";\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(AccountActivityWorker.TASK_QUEUE)\n            .setWorkflowId(to)\n            .build();\n    AccountTransferWorkflow transferWorkflow =\n        workflowClient.newWorkflowStub(AccountTransferWorkflow.class, options);\n    // Signal with start sends a signal to a workflow starting it if not yet running\n    BatchRequest request = workflowClient.newSignalWithStartRequest();\n    request.add(transferWorkflow::deposit, to, BATCH_SIZE);\n    request.add(transferWorkflow::withdraw, from, reference, amountCents);\n    workflowClient.signalWithStart(request);\n\n    System.out.printf(\"Transfer of %d cents from %s to %s requested\", amountCents, from, to);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/Account.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface Account {\n\n  void deposit(String accountId, String referenceId, int amountCents);\n\n  void withdraw(String accountId, String referenceId, int amountCents);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/AccountActivityWorker.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class AccountActivityWorker {\n\n  public static final String TASK_QUEUE = \"AccountTransfer\";\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    Account account = new AccountImpl();\n    worker.registerActivitiesImplementations(account);\n\n    // Start all workers created by this factory.\n    factory.start();\n    System.out.println(\"Activity Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/AccountImpl.java",
    "content": "package io.temporal.samples.moneytransfer;\n\npublic class AccountImpl implements Account {\n\n  @Override\n  public void withdraw(String accountId, String referenceId, int amountCents) {\n    System.out.printf(\n        \"Withdraw to %s of %d cents requested. ReferenceId=%s\\n\",\n        accountId, amountCents, referenceId);\n  }\n\n  @Override\n  public void deposit(String accountId, String referenceId, int amountCents) {\n    System.out.printf(\n        \"Deposit to %s of %d cents requested. ReferenceId=%s\\n\",\n        accountId, amountCents, referenceId);\n    //    throw new RuntimeException(\"simulated\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorker.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport static io.temporal.samples.moneytransfer.AccountActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class AccountTransferWorker {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    // Get worker to poll the common task queue.\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    // gRPC stubs wrapper that talks to the temporal service.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(AccountTransferWorkflowImpl.class);\n    // Start all workers created by this factory.\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorkflow.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface AccountTransferWorkflow {\n  @WorkflowMethod\n  void transfer(String fromAccountId, String toAccountId, String referenceId, int amountCents);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/AccountTransferWorkflowImpl.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class AccountTransferWorkflowImpl implements AccountTransferWorkflow {\n\n  private final ActivityOptions options =\n      ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build();\n  private final Account account = Workflow.newActivityStub(Account.class, options);\n\n  @Override\n  public void transfer(\n      String fromAccountId, String toAccountId, String referenceId, int amountCents) {\n    account.withdraw(fromAccountId, referenceId, amountCents);\n    account.deposit(toAccountId, referenceId, amountCents);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/README.MD",
    "content": "The Money Transfer sample has three separate processes.\nOne to host Workflow Executions, another to host Activity Executions, and the third one to request transfers (start Workflow Executions).\n\nStart Workflow Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.AccountTransferWorker\n```\n\nStart Activity Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.AccountActivityWorker\n```\n\nExecute once per requested transfer:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.moneytransfer.TransferRequester\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/moneytransfer/TransferRequester.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport static io.temporal.samples.moneytransfer.AccountActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.util.Random;\nimport java.util.UUID;\n\npublic class TransferRequester {\n\n  @SuppressWarnings(\"CatchAndPrintStackTrace\")\n  public static void main(String[] args) {\n    String reference;\n    int amountCents;\n    if (args.length == 0) {\n      reference = UUID.randomUUID().toString();\n      amountCents = new Random().nextInt(5000);\n    } else {\n      reference = args[0];\n      amountCents = Integer.parseInt(args[1]);\n    }\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    // client that can be used to start and signal workflows\n    WorkflowClient workflowClient =\n        WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // now we can start running instances of the saga - its state will be persisted\n    WorkflowOptions options = WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build();\n    AccountTransferWorkflow transferWorkflow =\n        workflowClient.newWorkflowStub(AccountTransferWorkflow.class, options);\n    String from = \"account1\";\n    String to = \"account2\";\n    WorkflowClient.start(transferWorkflow::transfer, from, to, reference, amountCents);\n    System.out.printf(\"Transfer of %d cents from %s to %s requested\", amountCents, from, to);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/README.MD",
    "content": "# Nexus\n\nTemporal Nexus is a new feature of the Temporal platform designed to connect durable executions across team, namespace,\nregion, and cloud boundaries. It promotes a more modular architecture for sharing a subset of your team’s capabilities\nvia well-defined service API contracts for other teams to use, that abstract underlying Temporal primitives, like\nWorkflows, or execute arbitrary code.\n\nLearn more at [temporal.io/nexus](https://temporal.io/nexus).\n\nThis sample shows how to use Temporal for authoring a Nexus service and call it from a workflow.\n\n### Sample directory structure\n\n- [service](./service) - shared service definition\n- [caller](./caller) - caller workflows, worker, and starter\n- [handler](./handler) - handler workflow, operations, and worker\n- [options](./options) - command line argument parsing utility\n\n## Getting started locally\n\n### Get `temporal` CLI to enable local development\n\n1. Follow the instructions on the [docs\n   site](https://learn.temporal.io/getting_started/go/dev_environment/#set-up-a-local-temporal-service-for-development-with-temporal-cli)\n   to install Temporal CLI.\n\n> NOTE: The recommended version is at least v1.3.0.\n\n### Spin up environment\n\n#### Start temporal server\n\n> HTTP port is required for Nexus communications\n\n```\ntemporal server start-dev\n```\n\n### Initialize environment\n\nIn a separate terminal window\n\n#### Create caller and target namespaces\n\n```\ntemporal operator namespace create --namespace my-target-namespace\ntemporal operator namespace create --namespace my-caller-namespace\n```\n\n#### Create Nexus endpoint\n\n```\ntemporal operator nexus endpoint create \\\n  --name my-nexus-endpoint-name \\\n  --target-namespace my-target-namespace \\\n  --target-task-queue my-handler-task-queue \\\n  --description-file ./core/src/main/java/io/temporal/samples/nexus/service/description.md\n```\n\n## Getting started with a self-hosted service or Temporal Cloud\n\nNexus is currently available as\n[Public Preview](https://docs.temporal.io/evaluate/development-production-features/release-stages).\n\nSelf hosted users can [try Nexus\nout](https://github.com/temporalio/temporal/blob/main/docs/architecture/nexus.md#trying-nexus-out) in single cluster\ndeployments with server version 1.25.0.\n\n### Make Nexus calls across namespace boundaries\n\n> Instructions apply for local development, for Temporal Cloud or a self-hosted setups, supply the relevant [CLI\n> flags](./options/ClientOptions.java) to properly set up the connection.\n\nIn separate terminal windows:\n\n### Nexus handler worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexus.handler.HandlerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-target-namespace\"\n```\n\n### Nexus caller worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexus.caller.CallerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Start caller workflow\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexus.caller.CallerStarter \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Output\n\nwhich should result in:\n```\n[main] INFO  i.t.s.nexus.caller.CallerStarter - Workflow result: Nexus Echo 👋 \n[main] INFO  i.t.s.nexus.caller.CallerStarter - Workflow result: ¡Hola! Nexus 👋  \n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/CallerStarter.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerStarter {\n  private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class);\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build();\n    EchoCallerWorkflow echoWorkflow =\n        client.newWorkflowStub(EchoCallerWorkflow.class, workflowOptions);\n    WorkflowExecution execution = WorkflowClient.start(echoWorkflow::echo, \"Nexus Echo 👋\");\n    logger.info(\n        \"Started EchoCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\"Workflow result: {}\", echoWorkflow.echo(\"Nexus Echo 👋\"));\n    HelloCallerWorkflow helloWorkflow =\n        client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);\n    execution = WorkflowClient.start(helloWorkflow::hello, \"Nexus\", SampleNexusService.Language.EN);\n    logger.info(\n        \"Started HelloCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\n        \"Workflow result: {}\", helloWorkflow.hello(\"Nexus\", SampleNexusService.Language.ES));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/CallerWorker.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.util.Collections;\n\npublic class CallerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-caller-workflow-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                Collections.singletonMap(\n                    \"SampleNexusService\",\n                    NexusServiceOptions.newBuilder().setEndpoint(\"my-nexus-endpoint-name\").build()))\n            .build(),\n        EchoCallerWorkflowImpl.class,\n        HelloCallerWorkflowImpl.class);\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflow.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface EchoCallerWorkflow {\n  @WorkflowMethod\n  String echo(String message);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/EchoCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class EchoCallerWorkflowImpl implements EchoCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String echo(String message) {\n    return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflow.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloCallerWorkflow {\n  @WorkflowMethod\n  String hello(String message, SampleNexusService.Language language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/caller/HelloCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationHandle;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class HelloCallerWorkflowImpl implements HelloCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String hello(String message, SampleNexusService.Language language) {\n    NexusOperationHandle<SampleNexusService.HelloOutput> handle =\n        Workflow.startNexusOperation(\n            sampleNexusService::hello, new SampleNexusService.HelloInput(message, language));\n    // Optionally wait for the operation to be started. NexusOperationExecution will contain the\n    // operation token in case this operation is asynchronous.\n    handle.getExecution().get();\n    return handle.getResult().get().getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/EchoClient.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\npublic interface EchoClient {\n  SampleNexusService.EchoOutput echo(SampleNexusService.EchoInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/EchoClientImpl.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\n// Note that this is a class, not a Temporal worker. This is to demonstrate that Nexus services can\n// simply call a class instead of a worker for fast operations that don't need retry handling.\npublic class EchoClientImpl implements EchoClient {\n  @Override\n  public SampleNexusService.EchoOutput echo(SampleNexusService.EchoInput input) {\n    return new SampleNexusService.EchoOutput(input.getMessage());\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\n\npublic class HandlerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-handler-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class);\n    worker.registerNexusServiceImplementation(new SampleNexusServiceImpl());\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflow.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloHandlerWorkflow {\n  @WorkflowMethod\n  SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/HelloHandlerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\npublic class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow {\n  @Override\n  public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) {\n    switch (input.getLanguage()) {\n      case EN:\n        return new SampleNexusService.HelloOutput(\"Hello \" + input.getName() + \" 👋\");\n      case FR:\n        return new SampleNexusService.HelloOutput(\"Bonjour \" + input.getName() + \" 👋\");\n      case DE:\n        return new SampleNexusService.HelloOutput(\"Hallo \" + input.getName() + \" 👋\");\n      case ES:\n        return new SampleNexusService.HelloOutput(\"¡Hola! \" + input.getName() + \" 👋\");\n      case TR:\n        return new SampleNexusService.HelloOutput(\"Merhaba \" + input.getName() + \" 👋\");\n    }\n    throw ApplicationFailure.newFailure(\n        \"Unsupported language: \" + input.getLanguage(), \"UNSUPPORTED_LANGUAGE\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/handler/SampleNexusServiceImpl.java",
    "content": "package io.temporal.samples.nexus.handler;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.nexus.Nexus;\nimport io.temporal.nexus.WorkflowRunOperation;\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\n// To create a service implementation, annotate the class with @ServiceImpl and provide the\n// interface that the service implements. The service implementation class should have methods that\n// return OperationHandler that correspond to the operations defined in the service interface.\n@ServiceImpl(service = SampleNexusService.class)\npublic class SampleNexusServiceImpl {\n  private final EchoClient echoClient;\n\n  // The injected EchoClient makes this class unit-testable.\n  // The no-arg constructor provides a default; the second allows tests to inject a mock.\n  // If you are not using the sync call or do not need to mock a handler, then you will not\n  // need this constructor pairing.\n  public SampleNexusServiceImpl() {\n    this(new EchoClientImpl());\n  }\n\n  public SampleNexusServiceImpl(EchoClient echoClient) {\n    this.echoClient = echoClient;\n  }\n\n  // The Echo Nexus Service exemplifies making a synchronous call using OperationHandler.sync.\n  // In this case, it is calling the EchoClient class - not a workflow - and simply returning the\n  // result.\n  @OperationImpl\n  public OperationHandler<SampleNexusService.EchoInput, SampleNexusService.EchoOutput> echo() {\n    return OperationHandler.sync(\n        // The method is for making arbitrary short calls to other services or databases, or\n        // perform simple computations such as this one. Users can also access a workflow client by\n        // calling\n        // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as\n        // signaling, querying, or listing workflows.\n        (ctx, details, input) -> echoClient.echo(input));\n  }\n\n  @OperationImpl\n  public OperationHandler<SampleNexusService.HelloInput, SampleNexusService.HelloOutput> hello() {\n    // Use the WorkflowRunOperation.fromWorkflowMethod constructor, which is the easiest\n    // way to expose a workflow as an operation. To expose a workflow with a different input\n    // parameters then the operation or from an untyped stub, use the\n    // WorkflowRunOperation.fromWorkflowHandler constructor and the appropriate constructor method\n    // on WorkflowHandle.\n    return WorkflowRunOperation.fromWorkflowMethod(\n        (ctx, details, input) ->\n            Nexus.getOperationContext()\n                    .getWorkflowClient()\n                    .newWorkflowStub(\n                        HelloHandlerWorkflow.class,\n                        // Workflow IDs should typically be business meaningful IDs and are used to\n                        // dedupe workflow starts. For this example, we're using the request ID\n                        // allocated by Temporal when the caller workflow schedules\n                        // the operation, this ID is guaranteed to be stable across retries of this\n                        // operation.\n                        //\n                        // Task queue defaults to the task queue this operation is handled on.\n                        WorkflowOptions.newBuilder().setWorkflowId(details.getRequestId()).build())\n                ::hello);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/options/ClientOptions.java",
    "content": "package io.temporal.samples.nexus.options;\n\nimport io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.util.InsecureTrustManagerFactory;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport java.io.FileInputStream;\nimport java.io.FileNotFoundException;\nimport javax.net.ssl.SSLException;\nimport org.apache.commons.cli.*;\n\npublic class ClientOptions {\n\n  public static WorkflowClient getWorkflowClient(String[] args) {\n    return getWorkflowClient(args, WorkflowClientOptions.newBuilder());\n  }\n\n  public static WorkflowClient getWorkflowClient(\n      String[] args, WorkflowClientOptions.Builder clientOptions) {\n    Options options = new Options();\n    Option targetHostOption = new Option(\"target-host\", true, \"Host:port for the Temporal service\");\n    targetHostOption.setRequired(false);\n    options.addOption(targetHostOption);\n\n    Option namespaceOption = new Option(\"namespace\", true, \"Namespace to connect to\");\n    namespaceOption.setRequired(false);\n    options.addOption(namespaceOption);\n\n    Option serverRootCaOption =\n        new Option(\"server-root-ca-cert\", true, \"Optional path to root server CA cert\");\n    serverRootCaOption.setRequired(false);\n    options.addOption(serverRootCaOption);\n\n    Option clientCertOption =\n        new Option(\n            \"client-cert\", true, \"Optional path to client cert, mutually exclusive with API key\");\n    clientCertOption.setRequired(false);\n    options.addOption(clientCertOption);\n\n    Option clientKeyOption =\n        new Option(\n            \"client-key\", true, \"Optional path to client key, mutually exclusive with API key\");\n    clientKeyOption.setRequired(false);\n    options.addOption(clientKeyOption);\n\n    Option apiKeyOption =\n        new Option(\"api-key\", true, \"Optional API key, mutually exclusive with cert/key\");\n    apiKeyOption.setRequired(false);\n    options.addOption(apiKeyOption);\n\n    Option serverNameOption =\n        new Option(\n            \"server-name\", true, \"Server name to use for verifying the server's certificate\");\n    serverNameOption.setRequired(false);\n    options.addOption(serverNameOption);\n\n    Option insercureSkipVerifyOption =\n        new Option(\n            \"insecure-skip-verify\",\n            false,\n            \"Skip verification of the server's certificate and host name\");\n    insercureSkipVerifyOption.setRequired(false);\n    options.addOption(insercureSkipVerifyOption);\n\n    CommandLineParser parser = new DefaultParser();\n    HelpFormatter formatter = new HelpFormatter();\n    CommandLine cmd = null;\n\n    try {\n      cmd = parser.parse(options, args);\n    } catch (ParseException e) {\n      System.out.println(e.getMessage());\n      formatter.printHelp(\"utility-name\", options);\n\n      System.exit(1);\n    }\n\n    String targetHost = cmd.getOptionValue(\"target-host\", \"localhost:7233\");\n    String namespace = cmd.getOptionValue(\"namespace\", \"default\");\n    String serverRootCaCert = cmd.getOptionValue(\"server-root-ca-cert\", \"\");\n    String clientCert = cmd.getOptionValue(\"client-cert\", \"\");\n    String clientKey = cmd.getOptionValue(\"client-key\", \"\");\n    String serverName = cmd.getOptionValue(\"server-name\", \"\");\n    boolean insecureSkipVerify = cmd.hasOption(\"insecure-skip-verify\");\n    String apiKey = cmd.getOptionValue(\"api-key\", \"\");\n\n    // API key and client cert/key are mutually exclusive\n    if (!apiKey.isEmpty() && (!clientCert.isEmpty() || !clientKey.isEmpty())) {\n      throw new IllegalArgumentException(\"API key and client cert/key are mutually exclusive\");\n    }\n    WorkflowServiceStubsOptions.Builder serviceStubOptionsBuilder =\n        WorkflowServiceStubsOptions.newBuilder().setTarget(targetHost);\n    // Configure TLS if client cert and key are provided\n    if (!clientCert.isEmpty() || !clientKey.isEmpty()) {\n      if (clientCert.isEmpty() || clientKey.isEmpty()) {\n        throw new IllegalArgumentException(\"Both client-cert and client-key must be provided\");\n      }\n      try {\n        SslContextBuilder sslContext =\n            SslContextBuilder.forClient()\n                .keyManager(new FileInputStream(clientCert), new FileInputStream(clientKey));\n        if (serverRootCaCert != null && !serverRootCaCert.isEmpty()) {\n          sslContext.trustManager(new FileInputStream(serverRootCaCert));\n        }\n        if (insecureSkipVerify) {\n          sslContext.trustManager(InsecureTrustManagerFactory.INSTANCE);\n        }\n        serviceStubOptionsBuilder.setSslContext(GrpcSslContexts.configure(sslContext).build());\n      } catch (SSLException e) {\n        throw new RuntimeException(e);\n      } catch (FileNotFoundException e) {\n        throw new RuntimeException(e);\n      }\n      if (serverName != null && !serverName.isEmpty()) {\n        serviceStubOptionsBuilder.setChannelInitializer(c -> c.overrideAuthority(serverName));\n      }\n    }\n    // Configure API key if provided\n    if (!apiKey.isEmpty()) {\n      serviceStubOptionsBuilder.setEnableHttps(true);\n      serviceStubOptionsBuilder.addApiKey(() -> apiKey);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(serviceStubOptionsBuilder.build());\n    return WorkflowClient.newInstance(service, clientOptions.setNamespace(namespace).build());\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java",
    "content": "package io.temporal.samples.nexus.service;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.nexusrpc.Operation;\nimport io.nexusrpc.Service;\n\n@Service\npublic interface SampleNexusService {\n  enum Language {\n    EN,\n    FR,\n    DE,\n    ES,\n    TR\n  }\n\n  class HelloInput {\n    private final String name;\n    private final Language language;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public HelloInput(\n        @JsonProperty(\"name\") String name, @JsonProperty(\"language\") Language language) {\n      this.name = name;\n      this.language = language;\n    }\n\n    @JsonProperty(\"name\")\n    public String getName() {\n      return name;\n    }\n\n    @JsonProperty(\"language\")\n    public Language getLanguage() {\n      return language;\n    }\n  }\n\n  class HelloOutput {\n    private final String message;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public HelloOutput(@JsonProperty(\"message\") String message) {\n      this.message = message;\n    }\n\n    @JsonProperty(\"message\")\n    public String getMessage() {\n      return message;\n    }\n  }\n\n  class EchoInput {\n    private final String message;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public EchoInput(@JsonProperty(\"message\") String message) {\n      this.message = message;\n    }\n\n    @JsonProperty(\"message\")\n    public String getMessage() {\n      return message;\n    }\n  }\n\n  class EchoOutput {\n    private final String message;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public EchoOutput(@JsonProperty(\"message\") String message) {\n      this.message = message;\n    }\n\n    @JsonProperty(\"message\")\n    public String getMessage() {\n      return message;\n    }\n  }\n\n  @Operation\n  HelloOutput hello(HelloInput input);\n\n  @Operation\n  EchoOutput echo(EchoInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexus/service/description.md",
    "content": "## Service: [SampleNexusService](https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java)\n - operation: `echo`\n - operation: `hello`\n\nSee https://github.com/temporalio/samples-java/blob/main/core/src/main/java/io/temporal/samples/nexus/service/SampleNexusService.java for Input / Output types.\n\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/README.MD",
    "content": "# Nexus Cancellation\n\nThis sample shows how to cancel a Nexus operation from a caller workflow and specify a cancellation type. In this sample we will show using the `WAIT_REQUESTED` cancellation type, which allows the caller to return after the handler workflow has received the requested to be cancelled, but does not wait for the handler workflow to finish processing the cancellation request.\n\nTo run this sample, set up your environment following the instructions in the main [Nexus Sample](../nexus/README.md).\n\nNext, in separate terminal windows:\n\n### Nexus handler worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.handler.HandlerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-target-namespace\"\n```\n\n### Nexus caller worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.caller.CallerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Start caller workflow\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscancellation.caller.CallerStarter \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Output\n\nwhich should result in on the caller side:\n```\n14:33:52.810 i.t.s.n.caller.CallerStarter - Started workflow workflowId: 87e97bf0-ca8a-4ae6-a9dc-ae97e5c0ac41 runId: 01976b36-a524-71a1-b848-8eb385fec2c3 \n14:33:54.250 i.t.s.n.caller.CallerStarter - Workflow result: Hallo Nexus 👋 \n```\n\non the handler side:\n\n```\n14:33:54.177 INFO  i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow was cancelled successfully. \n14:33:56.167 INFO  i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow was cancelled successfully. \n14:33:57.172 INFO  i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow was cancelled successfully. \n14:33:57.176 INFO  i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow was cancelled successfully. \n```\n\nNotice the timing, the caller workflow returned before the handler workflow was cancelled. This is because of the use of `WAIT_REQUESTED` as the cancellation type in the Nexus operation. This means the caller didn't have to wait for the handler workflow to finish, but still guarantees the handler workflow will receive the cancellation request. "
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerStarter.java",
    "content": "package io.temporal.samples.nexuscancellation.caller;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerStarter {\n  private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class);\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build();\n    HelloCallerWorkflow helloWorkflow =\n        client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);\n    WorkflowExecution execution = WorkflowClient.start(helloWorkflow::hello, \"Nexus\");\n    logger.info(\n        \"Started workflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\"Workflow result: {}\", helloWorkflow.hello(\"Nexus\"));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/caller/CallerWorker.java",
    "content": "package io.temporal.samples.nexuscancellation.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.util.Collections;\n\npublic class CallerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-caller-workflow-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                Collections.singletonMap(\n                    SampleNexusService.class.getSimpleName(),\n                    NexusServiceOptions.newBuilder().setEndpoint(\"my-nexus-endpoint-name\").build()))\n            .build(),\n        HelloCallerWorkflowImpl.class);\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflow.java",
    "content": "package io.temporal.samples.nexuscancellation.caller;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloCallerWorkflow {\n  @WorkflowMethod\n  String hello(String message);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/caller/HelloCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexuscancellation.caller;\n\nimport static io.temporal.samples.nexus.service.SampleNexusService.Language.*;\n\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.failure.NexusOperationFailure;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.*;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\npublic class HelloCallerWorkflowImpl implements HelloCallerWorkflow {\n  public static final Logger log = Workflow.getLogger(HelloCallerWorkflowImpl.class);\n  private static final SampleNexusService.Language[] languages =\n      new SampleNexusService.Language[] {EN, FR, DE, ES, TR};\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      // Set the cancellation type to WAIT_REQUESTED. This means that the caller\n                      // will wait for the cancellation request to be received by the handler before\n                      // proceeding with the cancellation.\n                      //\n                      // By default, the caller would wait until the operation is completed.\n                      .setCancellationType(NexusOperationCancellationType.WAIT_REQUESTED)\n                      .build())\n              .build());\n\n  @Override\n  public String hello(String message) {\n    List<Promise<SampleNexusService.HelloOutput>> results = new ArrayList<>(languages.length);\n\n    /*\n     * Create our CancellationScope. Within this scope we call the nexus operation asynchronously\n     * hello method asynchronously for each of our defined languages.\n     */\n    CancellationScope scope =\n        Workflow.newCancellationScope(\n            () -> {\n              for (SampleNexusService.Language language : languages) {\n                results.add(\n                    Async.function(\n                        sampleNexusService::hello,\n                        new SampleNexusService.HelloInput(message, language)));\n              }\n            });\n\n    /*\n     * Execute all nexus operations within the CancellationScope. Note that this execution is\n     * non-blocking as the code inside our cancellation scope is also non-blocking.\n     */\n    scope.run();\n\n    // We use \"anyOf\" here to wait for one of the nexus operation invocations to return\n    SampleNexusService.HelloOutput result = Promise.anyOf(results).get();\n\n    // Trigger cancellation of all uncompleted nexus operations invocations within the cancellation\n    // scope\n    scope.cancel();\n    // Wait for all nexus operations to receive a cancellation request before\n    // proceeding.\n    //\n    // Note: Once the workflow completes any pending cancellation requests are dropped by the\n    // server. In general, it is a good practice to wait for all cancellation requests to be\n    // processed before completing the workflow.\n    for (Promise<SampleNexusService.HelloOutput> promise : results) {\n      try {\n        promise.get();\n      } catch (NexusOperationFailure e) {\n        // If the operation was cancelled, we can ignore the failure\n        if (e.getCause() instanceof CanceledFailure) {\n          log.info(\"Operation was cancelled\");\n          continue;\n        }\n        throw e;\n      }\n    }\n    return result.getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexuscancellation.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.handler.SampleNexusServiceImpl;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\n\npublic class HandlerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-handler-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class);\n    worker.registerNexusServiceImplementation(new SampleNexusServiceImpl());\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscancellation/handler/HelloHandlerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexuscancellation.handler;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.slf4j.Logger;\n\npublic class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow {\n  public static final Logger log = Workflow.getLogger(HelloHandlerWorkflowImpl.class);\n\n  @Override\n  public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) {\n    // Sleep for a random duration to simulate some work\n    try {\n      Workflow.sleep(Duration.ofSeconds(Workflow.newRandom().nextInt(5)));\n      switch (input.getLanguage()) {\n        case EN:\n          return new SampleNexusService.HelloOutput(\"Hello \" + input.getName() + \" 👋\");\n        case FR:\n          return new SampleNexusService.HelloOutput(\"Bonjour \" + input.getName() + \" 👋\");\n        case DE:\n          return new SampleNexusService.HelloOutput(\"Hallo \" + input.getName() + \" 👋\");\n        case ES:\n          return new SampleNexusService.HelloOutput(\"¡Hola! \" + input.getName() + \" 👋\");\n        case TR:\n          return new SampleNexusService.HelloOutput(\"Merhaba \" + input.getName() + \" 👋\");\n      }\n      throw ApplicationFailure.newFailure(\n          \"Unsupported language: \" + input.getLanguage(), \"UNSUPPORTED_LANGUAGE\");\n    } catch (CanceledFailure e) {\n      // Simulate some work after cancellation is requested\n      Workflow.newDetachedCancellationScope(\n              () -> Workflow.sleep(Duration.ofSeconds(Workflow.newRandom().nextInt(5))))\n          .run();\n      log.info(\"HelloHandlerWorkflow was cancelled successfully.\");\n      throw e;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/README.MD",
    "content": "# Nexus Context Propagation\n\nThis sample shows how to propagate MDC (Mapped Diagnostic Context) context values from Workflows to Nexus operations.\nNexus does not support `ContextPropagator` since the header format is not compatible. Users should look at `NexusMDCContextInterceptor` for propagating MDC context values.\n\nTo run this sample, set up your environment following the instructions in the main [Nexus Sample](../nexus/README.md).\n\nNext, in separate terminal windows:\n\n### Nexus handler worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscontextpropagation.handler.HandlerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-target-namespace\"\n```\n\n### Nexus caller worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscontextpropagation.caller.CallerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Start caller workflow\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexuscontextpropagation.caller.CallerStarter \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Output\n\nwhich should result in this on the caller side:\n```\nINFO  i.t.s.n.caller.CallerStarter - Started EchoCallerWorkflow workflowId: 7ac97cb9-b457-4052-af94-d82478c35c5e runId: 01954eb9-6963-7b52-9a1d-b74e64643846 \nINFO  i.t.s.n.caller.CallerStarter - Workflow result: Nexus Echo 👋 \nINFO  i.t.s.n.caller.CallerStarter - Started HelloCallerWorkflow workflowId: 9e0bc89c-5709-4742-b7c0-868464c2fccf runId: 01954eb9-6ae3-7d6d-b355-71545688309d \nINFO  i.t.s.n.caller.CallerStarter - Workflow result: Hello Nexus 👋 \n```\n\nAnd this on the handler side:\n```\nINFO  i.t.s.n.handler.SampleNexusServiceImpl - Echo called from a workflow with ID : 7ac97cb9-b457-4052-af94-d82478c35c5e \nINFO  i.t.s.n.h.HelloHandlerWorkflowImpl - HelloHandlerWorkflow called from a workflow with ID : 9e0bc89c-5709-4742-b7c0-868464c2fccf \n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerStarter.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.caller;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.caller.EchoCallerWorkflow;\nimport io.temporal.samples.nexus.caller.HelloCallerWorkflow;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.samples.nexuscontextpropagation.propagation.MDCContextPropagator;\nimport java.util.Collections;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerStarter {\n  private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class);\n\n  public static void main(String[] args) {\n    WorkflowClient client =\n        ClientOptions.getWorkflowClient(\n            args,\n            WorkflowClientOptions.newBuilder()\n                .setContextPropagators(Collections.singletonList(new MDCContextPropagator())));\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build();\n    EchoCallerWorkflow echoWorkflow =\n        client.newWorkflowStub(EchoCallerWorkflow.class, workflowOptions);\n    WorkflowExecution execution = WorkflowClient.start(echoWorkflow::echo, \"Nexus Echo 👋\");\n    logger.info(\n        \"Started EchoCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\"Workflow result: {}\", echoWorkflow.echo(\"Nexus Echo 👋\"));\n    HelloCallerWorkflow helloWorkflow =\n        client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);\n    execution = WorkflowClient.start(helloWorkflow::hello, \"Nexus\", SampleNexusService.Language.EN);\n    logger.info(\n        \"Started HelloCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\n        \"Workflow result: {}\", helloWorkflow.hello(\"Nexus\", SampleNexusService.Language.ES));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/CallerWorker.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexuscontextpropagation.propagation.NexusMDCContextInterceptor;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.util.Collections;\n\npublic class CallerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-caller-workflow-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory =\n        WorkerFactory.newInstance(\n            client,\n            WorkerFactoryOptions.newBuilder()\n                .setWorkerInterceptors(new NexusMDCContextInterceptor())\n                .build());\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                Collections.singletonMap(\n                    \"SampleNexusService\",\n                    NexusServiceOptions.newBuilder().setEndpoint(\"my-nexus-endpoint-name\").build()))\n            .build(),\n        EchoCallerWorkflowImpl.class,\n        HelloCallerWorkflowImpl.class);\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/EchoCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.caller;\n\nimport io.temporal.samples.nexus.caller.EchoCallerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.slf4j.MDC;\n\npublic class EchoCallerWorkflowImpl implements EchoCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String echo(String message) {\n    MDC.put(\"x-nexus-caller-workflow-id\", Workflow.getInfo().getWorkflowId());\n    return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/caller/HelloCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.caller;\n\nimport io.temporal.samples.nexus.caller.HelloCallerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationHandle;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.slf4j.MDC;\n\npublic class HelloCallerWorkflowImpl implements HelloCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String hello(String message, SampleNexusService.Language language) {\n    MDC.put(\"x-nexus-caller-workflow-id\", Workflow.getInfo().getWorkflowId());\n    NexusOperationHandle<SampleNexusService.HelloOutput> handle =\n        Workflow.startNexusOperation(\n            sampleNexusService::hello, new SampleNexusService.HelloInput(message, language));\n    // Optionally wait for the operation to be started. NexusOperationExecution will contain the\n    // operation token in case this operation is asynchronous.\n    handle.getExecution().get();\n    return handle.getResult().get().getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexuscontextpropagation.propagation.MDCContextPropagator;\nimport io.temporal.samples.nexuscontextpropagation.propagation.NexusMDCContextInterceptor;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.util.Collections;\n\npublic class HandlerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-handler-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client =\n        ClientOptions.getWorkflowClient(\n            args,\n            WorkflowClientOptions.newBuilder()\n                .setContextPropagators(Collections.singletonList(new MDCContextPropagator())));\n\n    WorkerFactory factory =\n        WorkerFactory.newInstance(\n            client,\n            WorkerFactoryOptions.newBuilder()\n                .setWorkerInterceptors(new NexusMDCContextInterceptor())\n                .build());\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class);\n    worker.registerNexusServiceImplementation(new SampleNexusServiceImpl());\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/HelloHandlerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.handler;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.Workflow;\nimport org.slf4j.Logger;\nimport org.slf4j.MDC;\n\npublic class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow {\n  public static final Logger log = Workflow.getLogger(HelloHandlerWorkflowImpl.class);\n\n  @Override\n  public SampleNexusService.HelloOutput hello(SampleNexusService.HelloInput input) {\n    if (MDC.get(\"x-nexus-caller-workflow-id\") != null) {\n      log.info(\n          \"HelloHandlerWorkflow called from a workflow with ID : {}\",\n          MDC.get(\"x-nexus-caller-workflow-id\"));\n    }\n    switch (input.getLanguage()) {\n      case EN:\n        return new SampleNexusService.HelloOutput(\"Hello \" + input.getName() + \" 👋\");\n      case FR:\n        return new SampleNexusService.HelloOutput(\"Bonjour \" + input.getName() + \" 👋\");\n      case DE:\n        return new SampleNexusService.HelloOutput(\"Hallo \" + input.getName() + \" 👋\");\n      case ES:\n        return new SampleNexusService.HelloOutput(\"¡Hola! \" + input.getName() + \" 👋\");\n      case TR:\n        return new SampleNexusService.HelloOutput(\"Merhaba \" + input.getName() + \" 👋\");\n    }\n    throw ApplicationFailure.newFailure(\n        \"Unsupported language: \" + input.getLanguage(), \"UNSUPPORTED_LANGUAGE\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/handler/SampleNexusServiceImpl.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.handler;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.nexus.Nexus;\nimport io.temporal.nexus.WorkflowRunOperation;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.slf4j.MDC;\n\n// To create a service implementation, annotate the class with @ServiceImpl and provide the\n// interface that the service implements. The service implementation class should have methods that\n// return OperationHandler that correspond to the operations defined in the service interface.\n@ServiceImpl(service = SampleNexusService.class)\npublic class SampleNexusServiceImpl {\n  private static final Logger logger = LoggerFactory.getLogger(SampleNexusServiceImpl.class);\n\n  @OperationImpl\n  public OperationHandler<SampleNexusService.EchoInput, SampleNexusService.EchoOutput> echo() {\n    // OperationHandler.sync is a meant for exposing simple RPC handlers.\n    return OperationHandler.sync(\n        // The method is for making arbitrary short calls to other services or databases, or\n        // perform simple computations such as this one. Users can also access a workflow client by\n        // calling\n        // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as\n        // signaling, querying, or listing workflows.\n        (ctx, details, input) -> {\n          if (MDC.get(\"x-nexus-caller-workflow-id\") != null) {\n            logger.info(\n                \"Echo called from a workflow with ID : {}\", MDC.get(\"x-nexus-caller-workflow-id\"));\n          }\n          return new SampleNexusService.EchoOutput(input.getMessage());\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<SampleNexusService.HelloInput, SampleNexusService.HelloOutput> hello() {\n    // Use the WorkflowRunOperation.fromWorkflowMethod constructor, which is the easiest\n    // way to expose a workflow as an operation. To expose a workflow with a different input\n    // parameters then the operation or from an untyped stub, use the\n    // WorkflowRunOperation.fromWorkflowHandler constructor and the appropriate constructor method\n    // on WorkflowHandle.\n    return WorkflowRunOperation.fromWorkflowMethod(\n        (ctx, details, input) ->\n            Nexus.getOperationContext()\n                    .getWorkflowClient()\n                    .newWorkflowStub(\n                        HelloHandlerWorkflow.class,\n                        // Workflow IDs should typically be business meaningful IDs and are used to\n                        // dedupe workflow starts.\n                        // For this example, we're using the request ID allocated by Temporal when\n                        // the\n                        // caller workflow schedules\n                        // the operation, this ID is guaranteed to be stable across retries of this\n                        // operation.\n                        //\n                        // Task queue defaults to the task queue this operation is handled on.\n                        WorkflowOptions.newBuilder().setWorkflowId(details.getRequestId()).build())\n                ::hello);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/propagation/MDCContextPropagator.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.propagation;\n\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.common.context.ContextPropagator;\nimport io.temporal.common.converter.DataConverter;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.slf4j.MDC;\n\npublic class MDCContextPropagator implements ContextPropagator {\n\n  @Override\n  public String getName() {\n    return this.getClass().getName();\n  }\n\n  @Override\n  public Object getCurrentContext() {\n    Map<String, String> context = new HashMap<>();\n    if (MDC.getCopyOfContextMap() == null) {\n      return context;\n    }\n    for (Map.Entry<String, String> entry : MDC.getCopyOfContextMap().entrySet()) {\n      if (entry.getKey().startsWith(\"x-nexus-\")) {\n        context.put(entry.getKey(), entry.getValue());\n      }\n    }\n    return context;\n  }\n\n  @Override\n  public void setCurrentContext(Object context) {\n    Map<String, String> contextMap = (Map<String, String>) context;\n    for (Map.Entry<String, String> entry : contextMap.entrySet()) {\n      MDC.put(entry.getKey(), entry.getValue());\n    }\n  }\n\n  @Override\n  public Map<String, Payload> serializeContext(Object context) {\n    Map<String, String> contextMap = (Map<String, String>) context;\n    Map<String, Payload> serializedContext = new HashMap<>();\n    for (Map.Entry<String, String> entry : contextMap.entrySet()) {\n      serializedContext.put(\n          entry.getKey(), DataConverter.getDefaultInstance().toPayload(entry.getValue()).get());\n    }\n    return serializedContext;\n  }\n\n  @Override\n  public Object deserializeContext(Map<String, Payload> context) {\n    Map<String, String> contextMap = new HashMap<>();\n    for (Map.Entry<String, Payload> entry : context.entrySet()) {\n      contextMap.put(\n          entry.getKey(),\n          DataConverter.getDefaultInstance()\n              .fromPayload(entry.getValue(), String.class, String.class));\n    }\n    return contextMap;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexuscontextpropagation/propagation/NexusMDCContextInterceptor.java",
    "content": "package io.temporal.samples.nexuscontextpropagation.propagation;\n\nimport io.nexusrpc.OperationException;\nimport io.nexusrpc.handler.OperationContext;\nimport io.temporal.common.interceptors.NexusOperationInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkerInterceptorBase;\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport java.util.Map;\nimport org.slf4j.MDC;\n\n/**\n * Propagates MDC context from the caller workflow to the Nexus service through the operation\n * headers.\n */\npublic class NexusMDCContextInterceptor extends WorkerInterceptorBase {\n  private static final String NEXUS_HEADER_PREFIX = \"x-nexus-\";\n\n  @Override\n  public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) {\n    return new WorkflowInboundCallsInterceptorNexusMDC(next);\n  }\n\n  public static class WorkflowInboundCallsInterceptorNexusMDC\n      extends io.temporal.common.interceptors.WorkflowInboundCallsInterceptorBase {\n    private final WorkflowInboundCallsInterceptor next;\n\n    public WorkflowInboundCallsInterceptorNexusMDC(WorkflowInboundCallsInterceptor next) {\n      super(next);\n      this.next = next;\n    }\n\n    @Override\n    public void init(WorkflowOutboundCallsInterceptor outboundCalls) {\n      next.init(new WorkflowOutboundCallsInterceptorNexusMDC(outboundCalls));\n    }\n  }\n\n  public static class WorkflowOutboundCallsInterceptorNexusMDC\n      extends io.temporal.common.interceptors.WorkflowOutboundCallsInterceptorBase {\n    private final WorkflowOutboundCallsInterceptor next;\n\n    public WorkflowOutboundCallsInterceptorNexusMDC(WorkflowOutboundCallsInterceptor next) {\n      super(next);\n      this.next = next;\n    }\n\n    @Override\n    public <R> ExecuteNexusOperationOutput<R> executeNexusOperation(\n        ExecuteNexusOperationInput<R> input) {\n      Map<String, String> contextMap = MDC.getCopyOfContextMap();\n      if (contextMap != null) {\n        Map<String, String> headerMap = input.getHeaders();\n        contextMap.forEach(\n            (k, v) -> {\n              if (k.startsWith(NEXUS_HEADER_PREFIX)) {\n                headerMap.put(k, v);\n              }\n            });\n      }\n      return next.executeNexusOperation(input);\n    }\n  }\n\n  @Override\n  public NexusOperationInboundCallsInterceptor interceptNexusOperation(\n      OperationContext context, NexusOperationInboundCallsInterceptor next) {\n    return new NexusOperationInboundCallsInterceptorNexusMDC(next);\n  }\n\n  private static class NexusOperationInboundCallsInterceptorNexusMDC\n      extends io.temporal.common.interceptors.NexusOperationInboundCallsInterceptorBase {\n    private final NexusOperationInboundCallsInterceptor next;\n\n    public NexusOperationInboundCallsInterceptorNexusMDC(\n        NexusOperationInboundCallsInterceptor next) {\n      super(next);\n      this.next = next;\n    }\n\n    @Override\n    public StartOperationOutput startOperation(StartOperationInput input)\n        throws OperationException {\n      input\n          .getOperationContext()\n          .getHeaders()\n          .forEach(\n              (k, v) -> {\n                if (k.startsWith(NEXUS_HEADER_PREFIX)) {\n                  MDC.put(k, v);\n                }\n              });\n      return next.startOperation(input);\n    }\n\n    @Override\n    public CancelOperationOutput cancelOperation(CancelOperationInput input) {\n      input\n          .getOperationContext()\n          .getHeaders()\n          .forEach(\n              (k, v) -> {\n                if (k.startsWith(NEXUS_HEADER_PREFIX)) {\n                  MDC.put(k, v);\n                }\n              });\n      return next.cancelOperation(input);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/README.md",
    "content": "This sample shows how to expose a long-running Workflow's queries, updates, and signals as Nexus\noperations. There are two self-contained examples, each in its own directory:\n\n| | `callerpattern/` | `ondemandpattern/`                                           |\n|---|---|--------------------------------------------------------------|\n| **Pattern** | Signal an existing Workflow | Create and run Workflows on demand, and send signals to them |\n| **Who creates the Workflow?** | The handler worker starts it on boot | The caller starts it via a Nexus operation                   |\n| **Who knows the Workflow ID?** | Only the handler | The caller chooses and passes it in every operation          |\n| **Nexus service** | `NexusGreetingService` | `NexusRemoteGreetingService`                                 |\n\nEach directory is fully self-contained for clarity. The\n`GreetingWorkflow`, `GreetingWorkflowImpl`, `GreetingActivity` and `GreetingActivityImpl` classes are pretty much the same between the two — only the\nNexus service interface and its implementation differ. This highlights that the same Workflow can be\nexposed through Nexus in different ways depending on whether the caller needs lifecycle control.\n\nSee each directory's README for running instructions.\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/README.md",
    "content": "## Caller pattern\n\nThe handler worker starts a `GreetingWorkflow` for a User ID.\n`NexusGreetingServiceImpl` holds that ID and routes every Nexus operation to it. \nThe caller's input does not have that Workflow ID as the caller doesn't know it - but the caller sends in the User ID,\nand `NexusGreetingServiceImpl` knows how to get the desired Workflow ID from that User ID (see the getWorkflowId call).\n\nHandlerWorker is using the same getWorkflowId call to generate a Workflow ID from a User ID when it launches the Workflow.\n\nThe caller Workflow:\n1. Queries for supported languages (`getLanguages` — backed by a `@QueryMethod`)\n2. Changes the language to Arabic (`setLanguage` — backed by an `@UpdateMethod` that calls an activity)\n3. Confirms the change via a second query (`getLanguage`)\n4. Approves the Workflow (`approve` — backed by a `@SignalMethod`)\n\n### Running\n\nStart a Temporal server:\n\n```bash\ntemporal server start-dev\n```\n\nCreate the namespaces and Nexus endpoint:\n\n```bash\ntemporal operator namespace create --namespace nexus-messaging-handler-namespace\ntemporal operator namespace create --namespace nexus-messaging-caller-namespace\n\ntemporal operator nexus endpoint create \\\n  --name nexus-messaging-nexus-endpoint \\\n  --target-namespace nexus-messaging-handler-namespace \\\n  --target-task-queue nexus-messaging-handler-task-queue\n```\n\nThis sample loads connection settings from `ClientConfigProfile`. The\n`nexus-messaging-handler` and `nexus-messaging-caller` profiles are defined in\n`core/src/main/resources/config.toml`. You can override settings with environment\nvariables or by editing the TOML file (see the `envconfig` sample for details).\n\nIn one terminal, start the handler worker:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.callerpattern.handler.HandlerWorker\n```\n\nIn a second terminal, start the caller worker:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.callerpattern.caller.CallerWorker\n```\n\nIn a third terminal, run the following command to start the example:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.callerpattern.caller.CallerStarter\n```\n\nExpected output:\n\n```\nSupported languages: [CHINESE, ENGLISH]\nLanguage changed: ENGLISH -> ARABIC\nWorkflow approved\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/caller/CallerStarter.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class CallerStarter {\n\n  public static void main(String[] args) {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(CallerStarter.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CallerWorker.CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    CallerWorkflow workflow =\n        client.newWorkflowStub(\n            CallerWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"nexus-messaging-caller-\" + UUID.randomUUID())\n                .setTaskQueue(CallerWorker.TASK_QUEUE)\n                .build());\n\n    // Launch the worker, passing in an identifier which the Nexus service will use\n    // to find the matching workflow  (See NexusGreetingServiceImpl::getWorkflowId)\n    List<String> log = workflow.run(\"user-1\");\n    log.forEach(System.out::println);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/caller/CallerWorker.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerWorker {\n  private static final Logger logger = LoggerFactory.getLogger(CallerWorker.class);\n\n  static final String CONFIG_PROFILE = \"nexus-messaging-caller\";\n  public static final String TASK_QUEUE = \"nexus-messaging-caller-task-queue\";\n  static final String NEXUS_ENDPOINT = \"nexus-messaging-nexus-endpoint\";\n\n  public static void main(String[] args) throws InterruptedException {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(CallerWorker.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                // The key must match the @Service-annotated interface name.\n                Collections.singletonMap(\n                    \"NexusGreetingService\",\n                    NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build()))\n            .build(),\n        CallerWorkflowImpl.class);\n\n    factory.start();\n    logger.info(\"Caller worker started, ctrl+c to exit\");\n    Thread.currentThread().join();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/caller/CallerWorkflow.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.caller;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.util.List;\n\n@WorkflowInterface\npublic interface CallerWorkflow {\n  @WorkflowMethod\n  List<String> run(String userId);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/caller/CallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.caller;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.NexusGreetingService;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\npublic class CallerWorkflowImpl implements CallerWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(CallerWorkflowImpl.class);\n\n  // The endpoint is configured at the worker level in CallerWorker; only operation options are\n  // set here.\n  NexusGreetingService greetingService =\n      Workflow.newNexusServiceStub(\n          NexusGreetingService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public List<String> run(String userId) {\n\n    // Messages in the log array are passed back to the caller who will then log them to report what\n    // is happening.\n    // The same message is also logged for demo purposes, so that things are visible in the caller\n    // workflow output.\n    List<String> log = new ArrayList<>();\n\n    // Call a Nexus operation backed by a query against the entity workflow.\n    // The workflow must already be running on the handler, otherwise you will\n    // get an error saying the workflow has already terminated.\n    NexusGreetingService.GetLanguagesOutput languagesOutput =\n        greetingService.getLanguages(new NexusGreetingService.GetLanguagesInput(false, userId));\n    log.add(\"Supported languages: \" + languagesOutput.getLanguages());\n    logger.info(\"Supported languages: {}\", languagesOutput.getLanguages());\n\n    // Following are examples for each of the three messaging types -\n    // update, query, then signal.\n\n    // Call a Nexus operation backed by an update against the entity workflow.\n    Language previousLanguage =\n        greetingService.setLanguage(\n            new NexusGreetingService.SetLanguageInput(Language.ARABIC, userId));\n\n    // Call a Nexus operation backed by a query to confirm the language change.\n    Language currentLanguage =\n        greetingService.getLanguage(new NexusGreetingService.GetLanguageInput(userId));\n    if (currentLanguage != Language.ARABIC) {\n      throw ApplicationFailure.newFailure(\n          \"Expected language ARABIC, got \" + currentLanguage, \"AssertionError\");\n    }\n\n    log.add(\"Language changed: \" + previousLanguage.name() + \" -> \" + Language.ARABIC.name());\n    logger.info(\"Language changed from {} to {}\", previousLanguage, Language.ARABIC);\n\n    // Call a Nexus operation backed by a signal against the entity workflow.\n    greetingService.approve(new NexusGreetingService.ApproveInput(\"caller\", userId));\n    log.add(\"Workflow approved\");\n    logger.info(\"Workflow approved\");\n\n    return log;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/GreetingActivity.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\n\n@ActivityInterface\npublic interface GreetingActivity {\n  // Simulates a call to a remote greeting service. Returns null if the language is not supported.\n  @ActivityMethod\n  String callGreetingService(Language language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/GreetingActivityImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\nimport java.util.EnumMap;\nimport java.util.Map;\n\npublic class GreetingActivityImpl implements GreetingActivity {\n\n  private static final Map<Language, String> GREETINGS = new EnumMap<>(Language.class);\n\n  static {\n    GREETINGS.put(Language.ARABIC, \"مرحبا بالعالم\");\n    GREETINGS.put(Language.CHINESE, \"你好，世界\");\n    GREETINGS.put(Language.ENGLISH, \"Hello, world\");\n    GREETINGS.put(Language.FRENCH, \"Bonjour, monde\");\n    GREETINGS.put(Language.HINDI, \"नमस्ते दुनिया\");\n    GREETINGS.put(Language.PORTUGUESE, \"Olá mundo\");\n    GREETINGS.put(Language.SPANISH, \"Hola mundo\");\n  }\n\n  @Override\n  public String callGreetingService(Language language) {\n    return GREETINGS.get(language);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/GreetingWorkflow.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.NexusGreetingService;\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.UpdateValidatorMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * A long-running \"entity\" workflow that backs the NexusGreetingService Nexus operations. The\n * workflow exposes queries, an update, and a signal. These are private implementation details of\n * the Nexus service: the caller only interacts via Nexus operations.\n */\n@WorkflowInterface\npublic interface GreetingWorkflow {\n\n  @WorkflowMethod\n  String run();\n\n  // Returns the languages currently supported by the workflow.\n  @QueryMethod\n  NexusGreetingService.GetLanguagesOutput getLanguages(\n      NexusGreetingService.GetLanguagesInput input);\n\n  // Returns the currently active language.\n  @QueryMethod\n  Language getLanguage();\n\n  // Approves the workflow, allowing it to complete.\n  @SignalMethod\n  void approve(NexusGreetingService.ApproveInput input);\n\n  // Changes the active language synchronously (only supports languages already in the greetings\n  // map).\n  @UpdateMethod\n  Language setLanguage(NexusGreetingService.SetLanguageInput input);\n\n  @UpdateValidatorMethod(updateName = \"setLanguage\")\n  void validateSetLanguage(NexusGreetingService.SetLanguageInput input);\n\n  // Changes the active language, calling an activity to fetch a greeting for new languages.\n  @UpdateMethod\n  Language setLanguageUsingActivity(NexusGreetingService.SetLanguageInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/GreetingWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.NexusGreetingService;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.slf4j.Logger;\n\npublic class GreetingWorkflowImpl implements GreetingWorkflow {\n\n  private boolean approvedForRelease = false;\n  private final Map<Language, String> greetings = new EnumMap<>(Language.class);\n  private Language language = Language.ENGLISH;\n\n  private static final Logger logger = Workflow.getLogger(GreetingWorkflowImpl.class);\n\n  private final GreetingActivity greetingActivity =\n      Workflow.newActivityStub(\n          GreetingActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  public GreetingWorkflowImpl() {\n    greetings.put(Language.CHINESE, \"你好，世界\");\n    greetings.put(Language.ENGLISH, \"Hello, world\");\n  }\n\n  @Override\n  public String run() {\n    // Wait until approved and all in-flight update handlers have finished.\n    Workflow.await(() -> approvedForRelease && Workflow.isEveryHandlerFinished());\n    return greetings.get(language);\n  }\n\n  @Override\n  public NexusGreetingService.GetLanguagesOutput getLanguages(\n      NexusGreetingService.GetLanguagesInput input) {\n    List<Language> result;\n    if (input.isIncludeUnsupported()) {\n      result = new ArrayList<>(Arrays.asList(Language.values()));\n    } else {\n      result = new ArrayList<>(greetings.keySet());\n    }\n    Collections.sort(result);\n    return new NexusGreetingService.GetLanguagesOutput(result);\n  }\n\n  @Override\n  public Language getLanguage() {\n    return language;\n  }\n\n  @Override\n  public void approve(NexusGreetingService.ApproveInput input) {\n    logger.info(\n        \"Approval signal received for workflow {}\",\n        NexusGreetingServiceImpl.getWorkflowId(input.getUserId()));\n    approvedForRelease = true;\n  }\n\n  @Override\n  public Language setLanguage(NexusGreetingService.SetLanguageInput input) {\n    logger.info(\n        \"setLanguage update received for workflow {}\",\n        NexusGreetingServiceImpl.getWorkflowId(input.getUserId()));\n    Language previous = language;\n    language = input.getLanguage();\n    return previous;\n  }\n\n  @Override\n  public void validateSetLanguage(NexusGreetingService.SetLanguageInput input) {\n    logger.info(\n        \"validateSetLanguage called for workflow {}\",\n        NexusGreetingServiceImpl.getWorkflowId(input.getUserId()));\n    if (!greetings.containsKey(input.getLanguage())) {\n      throw new IllegalArgumentException(input.getLanguage().name() + \" is not supported\");\n    }\n  }\n\n  @Override\n  public Language setLanguageUsingActivity(NexusGreetingService.SetLanguageInput input) {\n    if (!greetings.containsKey(input.getLanguage())) {\n      String greeting = greetingActivity.callGreetingService(input.getLanguage());\n      if (greeting == null) {\n        throw ApplicationFailure.newFailure(\n            \"Greeting service does not support \" + input.getLanguage().name(),\n            \"UnsupportedLanguage\");\n      }\n      greetings.put(input.getLanguage(), greeting);\n    }\n    Language previous = language;\n    language = input.getLanguage();\n    return previous;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowExecutionAlreadyStarted;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.nio.file.Paths;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class HandlerWorker {\n  private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class);\n\n  static final String CONFIG_PROFILE = \"nexus-messaging-handler\";\n  public static final String TASK_QUEUE = \"nexus-messaging-handler-task-queue\";\n  static final String USER_ID = \"user-1\";\n\n  public static void main(String[] args) throws InterruptedException {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(HandlerWorker.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Start the long-running entity workflow that backs the Nexus service, if not already running.\n    // Create a workflow ID derived from the given user ID.\n    // This would be for a process that would create a workflow for each UserID,\n    // if you had a single long running workflow for all users then you could\n    // remove all the USER_IDs from the inputs and just make everything refer\n    // to a single workflow ID.\n    String workflowId = NexusGreetingServiceImpl.getWorkflowId(USER_ID);\n\n    GreetingWorkflow greetingWorkflow =\n        client.newWorkflowStub(\n            GreetingWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(workflowId)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    try {\n      WorkflowClient.start(greetingWorkflow::run);\n      logger.info(\"Started greeting workflow: {}\", workflowId);\n    } catch (WorkflowExecutionAlreadyStarted e) {\n      logger.info(\"Greeting workflow already running: {}\", workflowId);\n    }\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new GreetingActivityImpl());\n    worker.registerNexusServiceImplementation(new NexusGreetingServiceImpl());\n\n    factory.start();\n    logger.info(\"Handler worker started, ctrl+c to exit\");\n    Thread.currentThread().join();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/handler/NexusGreetingServiceImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.handler;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.nexus.Nexus;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.callerpattern.service.NexusGreetingService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Nexus operation handler implementation. Each operation receives a userId, which is mapped to a\n * workflow ID using {@link #WORKFLOW_ID_PREFIX}. The operations are synchronous because queries and\n * updates against a running workflow complete quickly.\n */\n@ServiceImpl(service = NexusGreetingService.class)\npublic class NexusGreetingServiceImpl {\n\n  private static final Logger logger = LoggerFactory.getLogger(NexusGreetingServiceImpl.class);\n\n  static final String WORKFLOW_ID_PREFIX = \"GreetingWorkflow_for_\";\n\n  // This example assumes you might have multiple workflows, one for each user.\n  // If you had a single workflow for all users, then you could remove the\n  // getWorkflowId method, remove the user ID from each input, and just\n  // use the single workflow ID in the getWorkflowStub method below.\n  public static String getWorkflowId(String userId) {\n    return WORKFLOW_ID_PREFIX + userId;\n  }\n\n  private GreetingWorkflow getWorkflowStub(String userId) {\n    return Nexus.getOperationContext()\n        .getWorkflowClient()\n        .newWorkflowStub(GreetingWorkflow.class, getWorkflowId(userId));\n  }\n\n  @OperationImpl\n  public OperationHandler<\n          NexusGreetingService.GetLanguagesInput, NexusGreetingService.GetLanguagesOutput>\n      getLanguages() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Query for GetLanguages was received for user {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId()).getLanguages(input);\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<NexusGreetingService.GetLanguageInput, Language> getLanguage() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Query for GetLanguage was received for user {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId()).getLanguage();\n        });\n  }\n\n  // Routes to setLanguageUsingActivity (not setLanguage) so that new languages not already in the\n  // greetings map can be fetched via an activity.\n  @OperationImpl\n  public OperationHandler<NexusGreetingService.SetLanguageInput, Language> setLanguage() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Update for SetLanguage was received for user {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId()).setLanguageUsingActivity(input);\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<NexusGreetingService.ApproveInput, NexusGreetingService.ApproveOutput>\n      approve() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Signal for Approve was received for user {}\", input.getUserId());\n          getWorkflowStub(input.getUserId()).approve(input);\n          return new NexusGreetingService.ApproveOutput();\n        });\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/service/Language.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.service;\n\npublic enum Language {\n  ARABIC,\n  CHINESE,\n  ENGLISH,\n  FRENCH,\n  HINDI,\n  PORTUGUESE,\n  SPANISH\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/callerpattern/service/NexusGreetingService.java",
    "content": "package io.temporal.samples.nexusmessaging.callerpattern.service;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.nexusrpc.Operation;\nimport io.nexusrpc.Service;\nimport java.util.List;\n\n/**\n * Nexus service definition. Shared between the handler and caller. The caller uses this to create a\n * type-safe Nexus client stub; the handler implements the operations.\n */\n@Service\npublic interface NexusGreetingService {\n\n  class GetLanguagesInput {\n    private final boolean includeUnsupported;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguagesInput(\n        @JsonProperty(\"includeUnsupported\") boolean includeUnsupported,\n        @JsonProperty(\"userId\") String userId) {\n      this.includeUnsupported = includeUnsupported;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"includeUnsupported\")\n    public boolean isIncludeUnsupported() {\n      return includeUnsupported;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class GetLanguagesOutput {\n    private final List<Language> languages;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguagesOutput(@JsonProperty(\"languages\") List<Language> languages) {\n      this.languages = languages;\n    }\n\n    @JsonProperty(\"languages\")\n    public List<Language> getLanguages() {\n      return languages;\n    }\n  }\n\n  class GetLanguageInput {\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguageInput(@JsonProperty(\"userId\") String userId) {\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class ApproveInput {\n    private final String name;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ApproveInput(@JsonProperty(\"name\") String name, @JsonProperty(\"userId\") String userId) {\n      this.name = name;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"name\")\n    public String getName() {\n      return name;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)\n  class ApproveOutput {\n    @JsonCreator\n    public ApproveOutput() {}\n  }\n\n  class SetLanguageInput {\n    private final Language language;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public SetLanguageInput(\n        @JsonProperty(\"language\") Language language, @JsonProperty(\"userId\") String userId) {\n      this.language = language;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"language\")\n    public Language getLanguage() {\n      return language;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  // Returns the languages supported by the greeting workflow.\n  @Operation\n  GetLanguagesOutput getLanguages(GetLanguagesInput input);\n\n  // Returns the currently active language.\n  @Operation\n  Language getLanguage(GetLanguageInput input);\n\n  // Changes the active language, returning the previous one.\n  @Operation\n  Language setLanguage(SetLanguageInput input);\n\n  // Approves the workflow, allowing it to complete.\n  @Operation\n  ApproveOutput approve(ApproveInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/README.md",
    "content": "## On-demand pattern\n\nNo Workflow is pre-started. The caller creates and controls Workflow instances through Nexus\noperations. `NexusRemoteGreetingService` adds a `runFromRemote` operation that starts a new\n`GreetingWorkflow`, and every other operation includes a `workflowId` so the handler knows which\ninstance to target.\n\nThe caller Workflow:\n1. Starts two remote `GreetingWorkflow` instances via `runFromRemote` (backed by `WorkflowRunOperation`)\n2. Queries each for supported languages\n3. Changes the language on each (Arabic and Hindi)\n4. Confirms the changes via queries\n5. Approves both Workflows\n6. Waits for each to complete and returns their results\n\n### Running\n\nStart a Temporal server:\n\n```bash\ntemporal server start-dev\n```\n\nCreate the namespaces and Nexus endpoint:\n\n```bash\ntemporal operator namespace create --namespace nexus-messaging-handler-namespace\ntemporal operator namespace create --namespace nexus-messaging-caller-namespace\n\ntemporal operator nexus endpoint create \\\n  --name nexus-messaging-nexus-endpoint \\\n  --target-namespace nexus-messaging-handler-namespace \\\n  --target-task-queue nexus-messaging-handler-task-queue\n```\n\nThis sample loads connection settings from `ClientConfigProfile`. The\n`nexus-messaging-handler` and `nexus-messaging-caller` profiles are defined in\n`core/src/main/resources/config.toml`. You can override settings with environment\nvariables or by editing the TOML file (see the `envconfig` sample for details).\n\nIn one terminal, start the handler worker:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.ondemandpattern.handler.HandlerWorker\n```\n\nIn a second terminal, start the caller worker:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.ondemandpattern.caller.CallerRemoteWorker\n```\n\nIn a third terminal, run the following command to start the example:\n\n```bash\n./gradlew -q :core:execute -PmainClass=io.temporal.samples.nexusmessaging.ondemandpattern.caller.CallerRemoteStarter\n```\n\nExpected output:\n\n```\nstarted remote greeting workflow: UserId One\nstarted remote greeting workflow: UserId Two\nSupported languages for UserId One: [CHINESE, ENGLISH]\nSupported languages for UserId Two: [CHINESE, ENGLISH]\nUserId One changed language: ENGLISH -> ARABIC\nUserId Two changed language: ENGLISH -> HINDI\nWorkflows approved\nWorkflow one result: مرحبا بالعالم\nWorkflow two result: नमस्ते दुनिया\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/caller/CallerRemoteStarter.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.nio.file.Paths;\nimport java.util.List;\nimport java.util.UUID;\n\npublic class CallerRemoteStarter {\n\n  public static void main(String[] args) {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(CallerRemoteStarter.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CallerRemoteWorker.CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    CallerRemoteWorkflow workflow =\n        client.newWorkflowStub(\n            CallerRemoteWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"nexus-messaging-remote-caller-\" + UUID.randomUUID())\n                .setTaskQueue(CallerRemoteWorker.TASK_QUEUE)\n                .build());\n\n    List<String> log = workflow.run();\n    log.forEach(System.out::println);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/caller/CallerRemoteWorker.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.nio.file.Paths;\nimport java.util.Collections;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerRemoteWorker {\n  private static final Logger logger = LoggerFactory.getLogger(CallerRemoteWorker.class);\n\n  static final String CONFIG_PROFILE = \"nexus-messaging-caller\";\n  public static final String TASK_QUEUE = \"nexus-messaging-caller-remote-task-queue\";\n  static final String NEXUS_ENDPOINT = \"nexus-messaging-nexus-endpoint\";\n\n  public static void main(String[] args) throws InterruptedException {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(CallerRemoteWorker.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                // The key must match the @Service-annotated interface name.\n                Collections.singletonMap(\n                    \"NexusRemoteGreetingService\",\n                    NexusServiceOptions.newBuilder().setEndpoint(NEXUS_ENDPOINT).build()))\n            .build(),\n        CallerRemoteWorkflowImpl.class);\n\n    factory.start();\n    logger.info(\"Caller remote worker started, ctrl+c to exit\");\n    Thread.currentThread().join();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/caller/CallerRemoteWorkflow.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.caller;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.util.List;\n\n@WorkflowInterface\npublic interface CallerRemoteWorkflow {\n  @WorkflowMethod\n  List<String> run();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/caller/CallerRemoteWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.caller;\n\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.NexusRemoteGreetingService;\nimport io.temporal.workflow.NexusOperationHandle;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\npublic class CallerRemoteWorkflowImpl implements CallerRemoteWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(CallerRemoteWorkflowImpl.class);\n\n  // This is going to create two workflows and send messages to them.\n  // We need to have an ID to differentiate so that Nexus knows how to name\n  // a workflow and then how to know the correct destination workflow.\n  private static final String REMOTE_WORKFLOW_ONE = \"UserId One\";\n  private static final String REMOTE_WORKFLOW_TWO = \"UserId Two\";\n\n  NexusRemoteGreetingService greetingRemoteServiceOne =\n      Workflow.newNexusServiceStub(\n          NexusRemoteGreetingService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n  NexusRemoteGreetingService greetingRemoteServiceTwo =\n      Workflow.newNexusServiceStub(\n          NexusRemoteGreetingService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public List<String> run() {\n    // Messages in the log array are passed back to the caller who will then log them to report what\n    // is happening.\n    // The same message is also logged for demo purposes, so that things are visible in the caller\n    // workflow output.\n    List<String> log = new ArrayList<>();\n\n    // Each call is performed twice in this example. This assumes there are two users we want\n    // to process. The first call starts two workflows, one for each user.\n    // Subsequent calls perform different actions between the two users.\n    // There are examples for each of the three messaging types -\n    // update, query, then signal.\n\n    // This is an Async Nexus operation — starts a workflow on the handler and returns a handle.\n    // Unlike the sync operations below (getLanguages, setLanguage, etc.), this does not block\n    // until the workflow completes. It is backed by WorkflowRunOperation on the handler side.\n    NexusOperationHandle<String> handleOne =\n        Workflow.startNexusOperation(\n            greetingRemoteServiceOne::runFromRemote,\n            new NexusRemoteGreetingService.RunFromRemoteInput(REMOTE_WORKFLOW_ONE));\n    // Wait for the operation to be started (workflow is now running on the handler).\n    handleOne.getExecution().get();\n    log.add(\"started remote greeting workflow: \" + REMOTE_WORKFLOW_ONE);\n    logger.info(\"started remote greeting workflow {}\", REMOTE_WORKFLOW_ONE);\n\n    NexusOperationHandle<String> handleTwo =\n        Workflow.startNexusOperation(\n            greetingRemoteServiceTwo::runFromRemote,\n            new NexusRemoteGreetingService.RunFromRemoteInput(REMOTE_WORKFLOW_TWO));\n    // Wait for the operation to be started (workflow is now running on the handler).\n    handleTwo.getExecution().get();\n    log.add(\"started remote greeting workflow: \" + REMOTE_WORKFLOW_TWO);\n    logger.info(\"started remote greeting workflow {}\", REMOTE_WORKFLOW_TWO);\n\n    // Query the remote workflow for supported languages.\n    NexusRemoteGreetingService.GetLanguagesOutput languagesOutput =\n        greetingRemoteServiceOne.getLanguages(\n            new NexusRemoteGreetingService.GetLanguagesInput(false, REMOTE_WORKFLOW_ONE));\n    log.add(\n        \"Supported languages for \" + REMOTE_WORKFLOW_ONE + \": \" + languagesOutput.getLanguages());\n    logger.info(\n        \"supported languages are {} for workflow {}\",\n        languagesOutput.getLanguages(),\n        REMOTE_WORKFLOW_ONE);\n\n    languagesOutput =\n        greetingRemoteServiceTwo.getLanguages(\n            new NexusRemoteGreetingService.GetLanguagesInput(false, REMOTE_WORKFLOW_TWO));\n    log.add(\n        \"Supported languages for \" + REMOTE_WORKFLOW_TWO + \": \" + languagesOutput.getLanguages());\n    logger.info(\n        \"supported languages are {} for workflow {}\",\n        languagesOutput.getLanguages(),\n        REMOTE_WORKFLOW_TWO);\n\n    // Update the language on the remote workflow.\n    Language previousLanguageOne =\n        greetingRemoteServiceOne.setLanguage(\n            new NexusRemoteGreetingService.SetLanguageInput(Language.ARABIC, REMOTE_WORKFLOW_ONE));\n\n    Language previousLanguageTwo =\n        greetingRemoteServiceTwo.setLanguage(\n            new NexusRemoteGreetingService.SetLanguageInput(Language.HINDI, REMOTE_WORKFLOW_TWO));\n\n    // Confirm the change by querying.\n    Language currentLanguage =\n        greetingRemoteServiceOne.getLanguage(\n            new NexusRemoteGreetingService.GetLanguageInput(REMOTE_WORKFLOW_ONE));\n    log.add(\n        REMOTE_WORKFLOW_ONE\n            + \" changed language: \"\n            + previousLanguageOne.name()\n            + \" -> \"\n            + currentLanguage.name());\n    logger.info(\n        \"Language changed from {} to {} for workflow {}\",\n        previousLanguageOne,\n        currentLanguage,\n        REMOTE_WORKFLOW_ONE);\n\n    currentLanguage =\n        greetingRemoteServiceTwo.getLanguage(\n            new NexusRemoteGreetingService.GetLanguageInput(REMOTE_WORKFLOW_TWO));\n    log.add(\n        REMOTE_WORKFLOW_TWO\n            + \" changed language: \"\n            + previousLanguageTwo.name()\n            + \" -> \"\n            + currentLanguage.name());\n    logger.info(\n        \"Language changed from {} to {} for workflow {}\",\n        previousLanguageTwo,\n        currentLanguage,\n        REMOTE_WORKFLOW_TWO);\n\n    // Approve the remote workflow so it can complete.\n    greetingRemoteServiceOne.approve(\n        new NexusRemoteGreetingService.ApproveInput(\"remote-caller\", REMOTE_WORKFLOW_ONE));\n    greetingRemoteServiceTwo.approve(\n        new NexusRemoteGreetingService.ApproveInput(\"remote-caller\", REMOTE_WORKFLOW_TWO));\n    log.add(\"Workflows approved\");\n\n    // Wait for the remote workflow to finish and return its result.\n    String result = handleOne.getResult().get();\n    log.add(\"Workflow one result: \" + result);\n\n    result = handleTwo.getResult().get();\n    log.add(\"Workflow two result: \" + result);\n    return log;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/GreetingActivity.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\n\n@ActivityInterface\npublic interface GreetingActivity {\n  // Simulates a call to a remote greeting service. Returns null if the language is not supported.\n  @ActivityMethod\n  String callGreetingService(Language language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/GreetingActivityImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\nimport java.util.EnumMap;\nimport java.util.Map;\n\npublic class GreetingActivityImpl implements GreetingActivity {\n\n  private static final Map<Language, String> GREETINGS = new EnumMap<>(Language.class);\n\n  static {\n    GREETINGS.put(Language.ARABIC, \"مرحبا بالعالم\");\n    GREETINGS.put(Language.CHINESE, \"你好，世界\");\n    GREETINGS.put(Language.ENGLISH, \"Hello, world\");\n    GREETINGS.put(Language.FRENCH, \"Bonjour, monde\");\n    GREETINGS.put(Language.HINDI, \"नमस्ते दुनिया\");\n    GREETINGS.put(Language.PORTUGUESE, \"Olá mundo\");\n    GREETINGS.put(Language.SPANISH, \"Hola mundo\");\n  }\n\n  @Override\n  public String callGreetingService(Language language) {\n    return GREETINGS.get(language);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/GreetingWorkflow.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.NexusRemoteGreetingService;\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.UpdateValidatorMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * A long-running \"entity\" workflow that backs the NexusRemoteGreetingService Nexus operations. The\n * workflow exposes queries, an update, and a signal. These are private implementation details of\n * the Nexus service: the caller only interacts via Nexus operations.\n */\n@WorkflowInterface\npublic interface GreetingWorkflow {\n\n  class ApproveInput {\n    private final String name;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ApproveInput(@JsonProperty(\"name\") String name) {\n      this.name = name;\n    }\n\n    @JsonProperty(\"name\")\n    public String getName() {\n      return name;\n    }\n  }\n\n  class GetLanguagesInput {\n    private final boolean includeUnsupported;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguagesInput(@JsonProperty(\"includeUnsupported\") boolean includeUnsupported) {\n      this.includeUnsupported = includeUnsupported;\n    }\n\n    @JsonProperty(\"includeUnsupported\")\n    public boolean isIncludeUnsupported() {\n      return includeUnsupported;\n    }\n  }\n\n  class SetLanguageInput {\n    private final Language language;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public SetLanguageInput(@JsonProperty(\"language\") Language language) {\n      this.language = language;\n    }\n\n    @JsonProperty(\"language\")\n    public Language getLanguage() {\n      return language;\n    }\n  }\n\n  @WorkflowMethod\n  String run();\n\n  // Returns the languages currently supported by the workflow.\n  @QueryMethod\n  NexusRemoteGreetingService.GetLanguagesOutput getLanguages(GetLanguagesInput input);\n\n  // Returns the currently active language.\n  @QueryMethod\n  Language getLanguage();\n\n  // Approves the workflow, allowing it to complete.\n  @SignalMethod\n  void approve(ApproveInput input);\n\n  // Changes the active language synchronously (only supports languages already in the greetings\n  // map).\n  @UpdateMethod\n  Language setLanguage(SetLanguageInput input);\n\n  @UpdateValidatorMethod(updateName = \"setLanguage\")\n  void validateSetLanguage(SetLanguageInput input);\n\n  // Changes the active language, calling an activity to fetch a greeting for new languages.\n  @UpdateMethod\n  Language setLanguageUsingActivity(SetLanguageInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/GreetingWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.NexusRemoteGreetingService;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.EnumMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.slf4j.Logger;\n\npublic class GreetingWorkflowImpl implements GreetingWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(GreetingWorkflowImpl.class);\n  private boolean approvedForRelease = false;\n  private final Map<Language, String> greetings = new EnumMap<>(Language.class);\n  private Language language = Language.ENGLISH;\n\n  private final GreetingActivity greetingActivity =\n      Workflow.newActivityStub(\n          GreetingActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  public GreetingWorkflowImpl() {\n    greetings.put(Language.CHINESE, \"你好，世界\");\n    greetings.put(Language.ENGLISH, \"Hello, world\");\n  }\n\n  @Override\n  public String run() {\n    // Wait until approved and all in-flight update handlers have finished.\n    Workflow.await(() -> approvedForRelease && Workflow.isEveryHandlerFinished());\n    return greetings.get(language);\n  }\n\n  @Override\n  public NexusRemoteGreetingService.GetLanguagesOutput getLanguages(\n      GreetingWorkflow.GetLanguagesInput input) {\n    List<Language> result;\n    if (input.isIncludeUnsupported()) {\n      result = new ArrayList<>(Arrays.asList(Language.values()));\n    } else {\n      result = new ArrayList<>(greetings.keySet());\n    }\n    Collections.sort(result);\n    return new NexusRemoteGreetingService.GetLanguagesOutput(result);\n  }\n\n  @Override\n  public Language getLanguage() {\n    return language;\n  }\n\n  @Override\n  public void approve(ApproveInput input) {\n    logger.info(\"Approval signal received\");\n    approvedForRelease = true;\n  }\n\n  @Override\n  public Language setLanguage(GreetingWorkflow.SetLanguageInput input) {\n    logger.info(\"setLanguage update received\");\n    Language previous = language;\n    language = input.getLanguage();\n    return previous;\n  }\n\n  @Override\n  public void validateSetLanguage(GreetingWorkflow.SetLanguageInput input) {\n    logger.info(\"validateSetLanguage called\");\n    if (!greetings.containsKey(input.getLanguage())) {\n      throw new IllegalArgumentException(input.getLanguage().name() + \" is not supported\");\n    }\n  }\n\n  @Override\n  public Language setLanguageUsingActivity(GreetingWorkflow.SetLanguageInput input) {\n    if (!greetings.containsKey(input.getLanguage())) {\n      String greeting = greetingActivity.callGreetingService(input.getLanguage());\n      if (greeting == null) {\n        throw ApplicationFailure.newFailure(\n            \"Greeting service does not support \" + input.getLanguage().name(),\n            \"UnsupportedLanguage\");\n      }\n      greetings.put(input.getLanguage(), greeting);\n    }\n    Language previous = language;\n    language = input.getLanguage();\n    return previous;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.envconfig.LoadClientConfigProfileOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.nio.file.Paths;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class HandlerWorker {\n  private static final Logger logger = LoggerFactory.getLogger(HandlerWorker.class);\n\n  static final String CONFIG_PROFILE = \"nexus-messaging-handler\";\n  public static final String TASK_QUEUE = \"nexus-messaging-handler-task-queue\";\n\n  public static void main(String[] args) throws InterruptedException {\n    ClientConfigProfile profile;\n    try {\n      String configFilePath =\n          Paths.get(HandlerWorker.class.getResource(\"/config.toml\").toURI()).toString();\n      profile =\n          ClientConfigProfile.load(\n              LoadClientConfigProfileOptions.newBuilder()\n                  .setConfigFilePath(configFilePath)\n                  .setConfigFileProfile(CONFIG_PROFILE)\n                  .build());\n    } catch (Exception e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new GreetingActivityImpl());\n    worker.registerNexusServiceImplementation(new NexusRemoteGreetingServiceImpl());\n\n    factory.start();\n    logger.info(\"Handler worker started, ctrl+c to exit\");\n    Thread.currentThread().join();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/handler/NexusRemoteGreetingServiceImpl.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.handler;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.nexus.Nexus;\nimport io.temporal.nexus.WorkflowHandle;\nimport io.temporal.nexus.WorkflowRunOperation;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.Language;\nimport io.temporal.samples.nexusmessaging.ondemandpattern.service.NexusRemoteGreetingService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/**\n * Nexus operation handler for the on-demand pattern. Each operation receives the target workflow ID\n * in its input, and {@code runFromRemote} starts a brand-new GreetingWorkflow.\n */\n@ServiceImpl(service = NexusRemoteGreetingService.class)\npublic class NexusRemoteGreetingServiceImpl {\n\n  private static final Logger logger =\n      LoggerFactory.getLogger(NexusRemoteGreetingServiceImpl.class);\n\n  static final String WORKFLOW_ID_PREFIX = \"GreetingWorkflow_for_\";\n\n  // This example assumes you might have multiple workflows, one for each user.\n  // If you had a single workflow for all users, then you could remove the\n  // getWorkflowId method, remove the user ID from each input, and just\n  // use the single workflow ID in the getWorkflowStub method below.\n  public static String getWorkflowId(String userId) {\n    return WORKFLOW_ID_PREFIX + userId;\n  }\n\n  private GreetingWorkflow getWorkflowStub(String userId) {\n    return Nexus.getOperationContext()\n        .getWorkflowClient()\n        .newWorkflowStub(GreetingWorkflow.class, getWorkflowId(userId));\n  }\n\n  // Starts a new GreetingWorkflow with the caller-specified workflow ID. This is an async\n  // Nexus operation backed by WorkflowRunOperation.\n  @OperationImpl\n  public OperationHandler<NexusRemoteGreetingService.RunFromRemoteInput, String> runFromRemote() {\n    return WorkflowRunOperation.fromWorkflowHandle(\n        (ctx, details, input) -> {\n          logger.info(\"RunFromRemote was received for userID {}\", input.getUserId());\n          return WorkflowHandle.fromWorkflowMethod(\n              Nexus.getOperationContext()\n                      .getWorkflowClient()\n                      .newWorkflowStub(\n                          GreetingWorkflow.class,\n                          WorkflowOptions.newBuilder()\n                              .setWorkflowId(getWorkflowId(input.getUserId()))\n                              .setTaskQueue(HandlerWorker.TASK_QUEUE)\n                              .build())\n                  ::run);\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<\n          NexusRemoteGreetingService.GetLanguagesInput,\n          NexusRemoteGreetingService.GetLanguagesOutput>\n      getLanguages() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Query for GetLanguages was received for userId {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId())\n              .getLanguages(new GreetingWorkflow.GetLanguagesInput(input.isIncludeUnsupported()));\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<NexusRemoteGreetingService.GetLanguageInput, Language> getLanguage() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Query for GetLanguage was received for userId {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId()).getLanguage();\n        });\n  }\n\n  // Uses setLanguageUsingActivity so that new languages are fetched via an activity.\n  @OperationImpl\n  public OperationHandler<NexusRemoteGreetingService.SetLanguageInput, Language> setLanguage() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Update for SetLanguage was received for userId {}\", input.getUserId());\n          return getWorkflowStub(input.getUserId())\n              .setLanguageUsingActivity(new GreetingWorkflow.SetLanguageInput(input.getLanguage()));\n        });\n  }\n\n  @OperationImpl\n  public OperationHandler<\n          NexusRemoteGreetingService.ApproveInput, NexusRemoteGreetingService.ApproveOutput>\n      approve() {\n    return OperationHandler.sync(\n        (ctx, details, input) -> {\n          logger.info(\"Signal for Approve was received for userId {}\", input.getUserId());\n          getWorkflowStub(input.getUserId())\n              .approve(new GreetingWorkflow.ApproveInput(input.getName()));\n          return new NexusRemoteGreetingService.ApproveOutput();\n        });\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/service/Language.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.service;\n\npublic enum Language {\n  ARABIC,\n  CHINESE,\n  ENGLISH,\n  FRENCH,\n  HINDI,\n  PORTUGUESE,\n  SPANISH\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmessaging/ondemandpattern/service/NexusRemoteGreetingService.java",
    "content": "package io.temporal.samples.nexusmessaging.ondemandpattern.service;\n\nimport com.fasterxml.jackson.annotation.JsonAutoDetect;\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.nexusrpc.Operation;\nimport io.nexusrpc.Service;\nimport java.util.List;\n\n/**\n * Nexus service definition for the on-demand pattern. Every operation includes a {@code userId} so\n * the caller controls which workflow instance is targeted though that, while the Nexus service\n * converts that UserId into a WorkflowId. This also exposes a {@code runFromRemote} operation that\n * starts a new GreetingWorkflow.\n */\n@Service\npublic interface NexusRemoteGreetingService {\n\n  class RunFromRemoteInput {\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public RunFromRemoteInput(@JsonProperty(\"userId\") String userId) {\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class GetLanguagesInput {\n    private final boolean includeUnsupported;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguagesInput(\n        @JsonProperty(\"includeUnsupported\") boolean includeUnsupported,\n        @JsonProperty(\"userId\") String userId) {\n      this.includeUnsupported = includeUnsupported;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"includeUnsupported\")\n    public boolean isIncludeUnsupported() {\n      return includeUnsupported;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)\n  class GetLanguageInput {\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguageInput(@JsonProperty(\"userId\") String userId) {\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class SetLanguageInput {\n    private final Language language;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public SetLanguageInput(\n        @JsonProperty(\"language\") Language language, @JsonProperty(\"userId\") String userId) {\n      this.language = language;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"language\")\n    public Language getLanguage() {\n      return language;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class ApproveInput {\n    private final String name;\n    private final String userId;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ApproveInput(@JsonProperty(\"name\") String name, @JsonProperty(\"userId\") String userId) {\n      this.name = name;\n      this.userId = userId;\n    }\n\n    @JsonProperty(\"name\")\n    public String getName() {\n      return name;\n    }\n\n    @JsonProperty(\"userId\")\n    public String getUserId() {\n      return userId;\n    }\n  }\n\n  class GetLanguagesOutput {\n    private final List<Language> languages;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public GetLanguagesOutput(@JsonProperty(\"languages\") List<Language> languages) {\n      this.languages = languages;\n    }\n\n    @JsonProperty(\"languages\")\n    public List<Language> getLanguages() {\n      return languages;\n    }\n  }\n\n  @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)\n  class ApproveOutput {\n    @JsonCreator\n    public ApproveOutput() {}\n  }\n\n  // Starts a new GreetingWorkflow for the given user ID. This is an asynchronous Nexus\n  // operation: the caller receives a handle and can wait for the workflow to complete.\n  @Operation\n  String runFromRemote(RunFromRemoteInput input);\n\n  // Returns the languages supported by the specified workflow.\n  @Operation\n  GetLanguagesOutput getLanguages(GetLanguagesInput input);\n\n  // Returns the currently active language of the specified workflow.\n  @Operation\n  Language getLanguage(GetLanguageInput input);\n\n  // Changes the active language on the specified workflow, returning the previous one.\n  @Operation\n  Language setLanguage(SetLanguageInput input);\n\n  // Approves the specified workflow, allowing it to complete.\n  @Operation\n  ApproveOutput approve(ApproveInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/README.MD",
    "content": "# Nexus Multiple Arguments Sample\n\nThis sample shows how to map a Nexus operation to a caller workflow that takes multiple input arguments using [WorkflowRunOperation.fromWorkflowHandle](https://javadoc.io/doc/io.temporal/temporal-sdk/latest/io/temporal/nexus/WorkflowRunOperation.html#fromWorkflowHandle(io.temporal.nexus.WorkflowHandleFactory)).\n\nTo run this sample, set up your environment following the instructions in the main [Nexus Sample](../nexus/README.md).\n\nIn separate terminal windows:\n\n### Nexus handler worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.handler.HandlerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-target-namespace\"\n```\n\n### Nexus caller worker\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.caller.CallerWorker \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Start caller workflow\n\n```\n./gradlew -q execute -PmainClass=io.temporal.samples.nexusmultipleargs.caller.CallerStarter \\\n    --args=\"-target-host localhost:7233 -namespace my-caller-namespace\"\n```\n\n### Output\n\nwhich should result in:\n```\n[main] INFO  i.t.s.nexus.caller.CallerStarter - Workflow result: Nexus Echo 👋 \n[main] INFO  i.t.s.nexus.caller.CallerStarter - Workflow result: ¡Hola! Nexus 👋  \n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerStarter.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.caller.CallerWorker;\nimport io.temporal.samples.nexus.caller.EchoCallerWorkflow;\nimport io.temporal.samples.nexus.caller.HelloCallerWorkflow;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class CallerStarter {\n  private static final Logger logger = LoggerFactory.getLogger(CallerStarter.class);\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(CallerWorker.DEFAULT_TASK_QUEUE_NAME).build();\n    EchoCallerWorkflow echoWorkflow =\n        client.newWorkflowStub(EchoCallerWorkflow.class, workflowOptions);\n    WorkflowExecution execution = WorkflowClient.start(echoWorkflow::echo, \"Nexus Echo 👋\");\n    logger.info(\n        \"Started EchoCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\"Workflow result: {}\", echoWorkflow.echo(\"Nexus Echo 👋\"));\n    HelloCallerWorkflow helloWorkflow =\n        client.newWorkflowStub(HelloCallerWorkflow.class, workflowOptions);\n    execution = WorkflowClient.start(helloWorkflow::hello, \"Nexus\", SampleNexusService.Language.EN);\n    logger.info(\n        \"Started HelloCallerWorkflow workflowId: {} runId: {}\",\n        execution.getWorkflowId(),\n        execution.getRunId());\n    logger.info(\n        \"Workflow result: {}\", helloWorkflow.hello(\"Nexus\", SampleNexusService.Language.ES));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/CallerWorker.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.util.Collections;\n\npublic class CallerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-caller-workflow-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(\n        WorkflowImplementationOptions.newBuilder()\n            .setNexusServiceOptions(\n                Collections.singletonMap(\n                    \"SampleNexusService\",\n                    NexusServiceOptions.newBuilder().setEndpoint(\"my-nexus-endpoint-name\").build()))\n            .build(),\n        EchoCallerWorkflowImpl.class,\n        HelloCallerWorkflowImpl.class);\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflow.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface EchoCallerWorkflow {\n  @WorkflowMethod\n  String echo(String message);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/EchoCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.samples.nexus.caller.EchoCallerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class EchoCallerWorkflowImpl implements EchoCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String echo(String message) {\n    return sampleNexusService.echo(new SampleNexusService.EchoInput(message)).getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflow.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloCallerWorkflow {\n  @WorkflowMethod\n  String hello(String message, SampleNexusService.Language language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/caller/HelloCallerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmultipleargs.caller;\n\nimport io.temporal.samples.nexus.caller.HelloCallerWorkflow;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.NexusOperationHandle;\nimport io.temporal.workflow.NexusOperationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class HelloCallerWorkflowImpl implements HelloCallerWorkflow {\n  SampleNexusService sampleNexusService =\n      Workflow.newNexusServiceStub(\n          SampleNexusService.class,\n          NexusServiceOptions.newBuilder()\n              .setOperationOptions(\n                  NexusOperationOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(10))\n                      .build())\n              .build());\n\n  @Override\n  public String hello(String message, SampleNexusService.Language language) {\n    NexusOperationHandle<SampleNexusService.HelloOutput> handle =\n        Workflow.startNexusOperation(\n            sampleNexusService::hello, new SampleNexusService.HelloInput(message, language));\n    // Optionally wait for the operation to be started. NexusOperationExecution will contain the\n    // operation token in case this operation is asynchronous.\n    handle.getExecution().get();\n    return handle.getResult().get().getMessage();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HandlerWorker.java",
    "content": "package io.temporal.samples.nexusmultipleargs.handler;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.samples.nexus.options.ClientOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\n\npublic class HandlerWorker {\n  public static final String DEFAULT_TASK_QUEUE_NAME = \"my-handler-task-queue\";\n\n  public static void main(String[] args) {\n    WorkflowClient client = ClientOptions.getWorkflowClient(args);\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    Worker worker = factory.newWorker(DEFAULT_TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(HelloHandlerWorkflowImpl.class);\n    worker.registerNexusServiceImplementation(new SampleNexusServiceImpl());\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflow.java",
    "content": "package io.temporal.samples.nexusmultipleargs.handler;\n\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloHandlerWorkflow {\n  @WorkflowMethod\n  SampleNexusService.HelloOutput hello(String name, SampleNexusService.Language language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/HelloHandlerWorkflowImpl.java",
    "content": "package io.temporal.samples.nexusmultipleargs.handler;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\npublic class HelloHandlerWorkflowImpl implements HelloHandlerWorkflow {\n  @Override\n  public SampleNexusService.HelloOutput hello(String name, SampleNexusService.Language language) {\n    switch (language) {\n      case EN:\n        return new SampleNexusService.HelloOutput(\"Hello \" + name + \" 👋\");\n      case FR:\n        return new SampleNexusService.HelloOutput(\"Bonjour \" + name + \" 👋\");\n      case DE:\n        return new SampleNexusService.HelloOutput(\"Hallo \" + name + \" 👋\");\n      case ES:\n        return new SampleNexusService.HelloOutput(\"¡Hola! \" + name + \" 👋\");\n      case TR:\n        return new SampleNexusService.HelloOutput(\"Merhaba \" + name + \" 👋\");\n    }\n    throw ApplicationFailure.newFailure(\n        \"Unsupported language: \" + language, \"UNSUPPORTED_LANGUAGE\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/nexusmultipleargs/handler/SampleNexusServiceImpl.java",
    "content": "package io.temporal.samples.nexusmultipleargs.handler;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.nexus.Nexus;\nimport io.temporal.nexus.WorkflowHandle;\nimport io.temporal.nexus.WorkflowRunOperation;\nimport io.temporal.samples.nexus.service.SampleNexusService;\n\n// To create a service implementation, annotate the class with @ServiceImpl and provide the\n// interface that the service implements. The service implementation class should have methods that\n// return OperationHandler that correspond to the operations defined in the service interface.\n@ServiceImpl(service = SampleNexusService.class)\npublic class SampleNexusServiceImpl {\n  @OperationImpl\n  public OperationHandler<SampleNexusService.EchoInput, SampleNexusService.EchoOutput> echo() {\n    // OperationHandler.sync is a meant for exposing simple RPC handlers.\n    return OperationHandler.sync(\n        // The method is for making arbitrary short calls to other services or databases, or\n        // perform simple computations such as this one. Users can also access a workflow client by\n        // calling\n        // Nexus.getOperationContext().getWorkflowClient(ctx) to make arbitrary calls such as\n        // signaling, querying, or listing workflows.\n        (ctx, details, input) -> new SampleNexusService.EchoOutput(input.getMessage()));\n  }\n\n  @OperationImpl\n  public OperationHandler<SampleNexusService.HelloInput, SampleNexusService.HelloOutput> hello() {\n    // If the operation input parameters are different from the workflow input parameters,\n    // use the WorkflowRunOperation.fromWorkflowHandler constructor and the appropriate constructor\n    // method on WorkflowHandle to map the Nexus input to the workflow parameters.\n    return WorkflowRunOperation.fromWorkflowHandle(\n        (ctx, details, input) ->\n            WorkflowHandle.fromWorkflowMethod(\n                Nexus.getOperationContext()\n                        .getWorkflowClient()\n                        .newWorkflowStub(\n                            HelloHandlerWorkflow.class,\n                            // Workflow IDs should typically be business meaningful IDs and are used\n                            // to\n                            // dedupe workflow starts.\n                            // For this example, we're using the request ID allocated by Temporal\n                            // when\n                            // the\n                            // caller workflow schedules\n                            // the operation, this ID is guaranteed to be stable across retries of\n                            // this\n                            // operation.\n                            //\n                            // Task queue defaults to the task queue this operation is handled on.\n                            WorkflowOptions.newBuilder()\n                                .setWorkflowId(details.getRequestId())\n                                .build())\n                    ::hello,\n                input.getName(),\n                input.getLanguage()));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/Packet.java",
    "content": "package io.temporal.samples.packetdelivery;\n\npublic class Packet {\n  private int id;\n  private String content;\n\n  public Packet() {}\n\n  public Packet(int id, String content) {\n    this.id = id;\n    this.content = content;\n  }\n\n  public int getId() {\n    return id;\n  }\n\n  public String getContent() {\n    return content;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketDelivery.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.workflow.*;\nimport java.time.Duration;\nimport org.slf4j.Logger;\n\npublic class PacketDelivery {\n  private Packet packet;\n  private boolean deliveryConfirmation = false;\n  private boolean needDeliveryConfirmation = false;\n  private CompletablePromise delivered = Workflow.newPromise();\n  private CancellationScope cancellationScope;\n\n  private Logger logger = Workflow.getLogger(this.getClass().getName());\n\n  private final PacketDeliveryActivities activities =\n      Workflow.newActivityStub(\n          PacketDeliveryActivities.class,\n          ActivityOptions.newBuilder()\n              .setStartToCloseTimeout(Duration.ofSeconds(5))\n              .setHeartbeatTimeout(Duration.ofSeconds(2))\n              .build());\n\n  private final PacketDeliveryActivities compensationActivities =\n      Workflow.newActivityStub(\n          PacketDeliveryActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(3)).build());\n\n  public PacketDelivery(Packet packet) {\n    this.packet = packet;\n    processDeliveryAsync();\n  }\n\n  public Promise<Void> getDelivered() {\n    return delivered;\n  }\n\n  public void processDeliveryAsync() {\n    delivered.completeFrom(Async.procedure(this::processDelivery));\n  }\n\n  public void processDelivery() {\n    cancellationScope =\n        Workflow.newCancellationScope(\n            () -> {\n              String deliveryConfirmationResult = \"\";\n              while (!deliveryConfirmationResult.equals(PacketUtils.COMPLETION_SUCCESS)) {\n                // Step 1 perform delivery\n                logger.info(\n                    \"** Performing delivery for packet: \"\n                        + packet.getId()\n                        + \" - \"\n                        + packet.getContent());\n                activities.performDelivery(packet);\n                // Step 2 wait for delivery confirmation\n                logger.info(\n                    \"** Delivery for packet: \"\n                        + packet.getId()\n                        + \" - \"\n                        + packet.getContent()\n                        + \" awaiting delivery confirmation\");\n                needDeliveryConfirmation = true;\n                Workflow.await(() -> deliveryConfirmation);\n                logger.info(\n                    \"** Delivery for packet: \"\n                        + packet.getId()\n                        + \" - \"\n                        + packet.getContent()\n                        + \" received confirmation\");\n                // Step 3 complete delivery processing\n                logger.info(\n                    \"** Completing delivery for packet: \"\n                        + packet.getId()\n                        + \" - \"\n                        + packet.getContent());\n                deliveryConfirmationResult = activities.completeDelivery(packet);\n                // Reset deliveryConfirmation and needDeliveryConfirmation\n                deliveryConfirmation = false;\n                needDeliveryConfirmation = false;\n              }\n            });\n\n    try {\n      cancellationScope.run();\n    } catch (Exception e) {\n      if (e instanceof ActivityFailure) {\n        ActivityFailure activityFailure = (ActivityFailure) e;\n        if (activityFailure.getCause() instanceof CanceledFailure) {\n          // Run compensation activity and complete\n          compensationActivities.compensateDelivery(packet);\n        }\n      }\n      // Just for show for example that cancel could come in while we are waiting on approval signal\n      // too\n      else if (e instanceof CanceledFailure) {\n        needDeliveryConfirmation = false;\n        // Run compensation activity and complete\n        compensationActivities.compensateDelivery(packet);\n      }\n      return;\n    }\n  }\n\n  public void confirmDelivery() {\n    this.deliveryConfirmation = true;\n  }\n\n  public void cancelDelivery(String reason) {\n    if (cancellationScope != null) {\n      cancellationScope.cancel(reason);\n    }\n  }\n\n  public boolean isNeedDeliveryConfirmation() {\n    return needDeliveryConfirmation;\n  }\n\n  public Packet getPacket() {\n    return packet;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketDeliveryActivities.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.activity.ActivityInterface;\nimport java.util.List;\n\n@ActivityInterface\npublic interface PacketDeliveryActivities {\n  List<Packet> generatePackets();\n\n  void performDelivery(Packet packet);\n\n  String completeDelivery(Packet packet);\n\n  String compensateDelivery(Packet packet);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketDeliveryActivitiesImpl.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.client.ActivityCompletionException;\nimport io.temporal.client.WorkflowClient;\nimport java.util.*;\n\npublic class PacketDeliveryActivitiesImpl implements PacketDeliveryActivities {\n  private List<Packet> packets =\n      Arrays.asList(\n          new Packet(1, \"books\"),\n          new Packet(2, \"jewelry\"),\n          new Packet(3, \"furniture\"),\n          new Packet(4, \"food\"),\n          new Packet(5, \"electronics\"));\n  private WorkflowClient client;\n\n  public PacketDeliveryActivitiesImpl(WorkflowClient client) {\n    this.client = client;\n  }\n\n  @Override\n  public List<Packet> generatePackets() {\n    return packets;\n  }\n\n  @Override\n  public void performDelivery(Packet packet) {\n    ActivityExecutionContext context = Activity.getExecutionContext();\n    System.out.println(\n        \"** Activity - Performing delivery for packet: \"\n            + packet.getId()\n            + \" with content: \"\n            + packet.getContent());\n    for (int i = 0; i < 4; i++) {\n      try {\n        // Perform the heartbeat. Used to notify the workflow that activity execution is alive\n        context.heartbeat(i);\n      } catch (ActivityCompletionException e) {\n        System.out.println(\n            \"** Activity - Canceling delivery activity for packet: \"\n                + packet.getId()\n                + \" with content: \"\n                + packet.getContent());\n        throw e;\n      }\n    }\n  }\n\n  @Override\n  public String completeDelivery(Packet packet) {\n    ActivityExecutionContext context = Activity.getExecutionContext();\n    System.out.println(\n        \"** Activity - Completing delivery for package: \"\n            + packet.getId()\n            + \" with content: \"\n            + packet.getContent());\n    for (int i = 0; i < 4; i++) {\n      try {\n        // Perform the heartbeat. Used to notify the workflow that activity execution is alive\n        context.heartbeat(i);\n      } catch (ActivityCompletionException e) {\n        System.out.println(\n            \"** Activity - Canceling complete delivery activity for packet: \"\n                + packet.getId()\n                + \" with content: \"\n                + packet.getContent());\n        throw e;\n      }\n    }\n    // For sample we just confirm\n    return randomCompletionDeliveryResult(packet);\n  }\n\n  @Override\n  public String compensateDelivery(Packet packet) {\n    System.out.println(\n        \"** Activity - Compensating delivery for package: \"\n            + packet.getId()\n            + \" with content: \"\n            + packet.getContent());\n    sleep(1);\n    return PacketUtils.COMPENSATION_COMPLETED;\n  }\n\n  /**\n   * For this sample activity completion result can drive if 1. Delivery confirmation is completed,\n   * in which case we complete delivery 2. Delivery confirmation is failed, in which case we run the\n   * delivery again 3. Delivery confirmation is cancelled, in which case we want to cancel delivery\n   * and perform \"cleanup activity\" Note that any delivery can cancel itself OR another delivery, so\n   * for example Furniure delivery can cancel the Food delivery. For sample we have some specific\n   * rules Which delivery can cancel which\n   */\n  private String randomCompletionDeliveryResult(Packet packet) {\n    Random random = new Random();\n    double randomValue = random.nextDouble();\n    if (randomValue < 0.10) { // 10% chance for delivery completion to be canceled\n      int toCancelDelivery = determineCancelRules(packet);\n      System.out.println(\n          \"** Activity - Delivery completion result for package: \"\n              + packet.getId()\n              + \" with content: \"\n              + packet.getContent()\n              + \": \"\n              + \"Cancelling delivery: \"\n              + toCancelDelivery);\n\n      // send cancellation signal for packet to be canceled\n      PacketDeliveryWorkflow packetWorkflow =\n          client.newWorkflowStub(\n              PacketDeliveryWorkflow.class,\n              Activity.getExecutionContext().getInfo().getWorkflowId());\n      packetWorkflow.cancelDelivery(toCancelDelivery, \"canceled from delivery \" + packet.getId());\n\n      return PacketUtils.COMPLETION_CANCELLED;\n    }\n    if (randomValue < 0.20) { // 20% chance for delivery completion to fail\n      System.out.println(\n          \"** Activity - Delivery completion result for package: \"\n              + packet.getId()\n              + \" with content: \"\n              + packet.getContent()\n              + \": \"\n              + \"Failed\");\n      return PacketUtils.COMPLETION_FAILURE;\n    }\n\n    System.out.println(\n        \"** Activity - Delivery completion result for package: \"\n            + packet.getId()\n            + \" with content: \"\n            + packet.getContent()\n            + \": \"\n            + \"Successful\");\n    return PacketUtils.COMPLETION_SUCCESS;\n  }\n\n  private void sleep(int seconds) {\n    try {\n      Thread.sleep(seconds * 1000L);\n    } catch (Exception e) {\n      System.out.println(e.getMessage());\n    }\n  }\n\n  /**\n   * Sample rules for canceling different deliveries We just rotate the list 1-5 (packet ids) by\n   * packet id and return first result\n   */\n  private int determineCancelRules(Packet packet) {\n    List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));\n    Collections.rotate(list, packet.getId());\n    System.out.println(\n        \"** Activity - Package delivery : \"\n            + packet.getId()\n            + \" canceling package delivery: \"\n            + list.get(0));\n    return list.get(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketDeliveryWorkflow.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.util.List;\n\n@WorkflowInterface\npublic interface PacketDeliveryWorkflow {\n  @WorkflowMethod\n  String execute();\n\n  @SignalMethod\n  void confirmDelivery(int deliveryId);\n\n  @SignalMethod\n  void cancelDelivery(int deliveryId, String reason);\n\n  @QueryMethod\n  List<Packet> deliveryConfirmationPackets();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketDeliveryWorkflowImpl.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.slf4j.Logger;\n\npublic class PacketDeliveryWorkflowImpl implements PacketDeliveryWorkflow {\n  private final Map<Integer, PacketDelivery> packetDeliveries = new HashMap<>();\n  private final Logger logger = Workflow.getLogger(this.getClass().getName());\n\n  private final PacketDeliveryActivities activities =\n      Workflow.newActivityStub(\n          PacketDeliveryActivities.class,\n          ActivityOptions.newBuilder()\n              .setStartToCloseTimeout(Duration.ofSeconds(5))\n              .setHeartbeatTimeout(Duration.ofSeconds(2))\n              .build());\n\n  @Override\n  public String execute() {\n    List<Promise<Void>> packetsDelivered = new ArrayList<>();\n    // Step 1 - upload initial packets to deliver\n    List<Packet> initialPackets = activities.generatePackets();\n    // Step 2 - set up delivery processing\n    for (Packet packet : initialPackets) {\n      PacketDelivery delivery = new PacketDelivery(packet);\n      packetDeliveries.put(packet.getId(), delivery);\n      packetsDelivered.add(delivery.getDelivered());\n    }\n\n    // Wait for all packet deliveries to complete\n    Promise.allOf(packetsDelivered).get();\n    return \"completed\";\n  }\n\n  @Override\n  public void confirmDelivery(int deliveryId) {\n    if (packetDeliveries.containsKey(deliveryId)) {\n      packetDeliveries.get(deliveryId).confirmDelivery();\n    }\n  }\n\n  @Override\n  public void cancelDelivery(int deliveryId, String reason) {\n    if (packetDeliveries.containsKey(deliveryId)) {\n      // Only makes sense to cancel if delivery is not done yet\n      if (!packetDeliveries.get(deliveryId).getDelivered().isCompleted()) {\n        logger.info(\"Sending cancellation for delivery : \" + deliveryId + \" and reason: \" + reason);\n        packetDeliveries.get(deliveryId).cancelDelivery(reason);\n      }\n      logger.info(\n          \"Bypassing sending cancellation for delivery : \"\n              + deliveryId\n              + \" and reason: \"\n              + reason\n              + \" because delivery already completed\");\n    }\n  }\n\n  @Override\n  public List<Packet> deliveryConfirmationPackets() {\n    List<Packet> confirmationPackets = new ArrayList<>();\n    packetDeliveries\n        .values()\n        .forEach(\n            p -> {\n              if (p.isNeedDeliveryConfirmation()) {\n                confirmationPackets.add(p.getPacket());\n              }\n            });\n    return confirmationPackets;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/PacketUtils.java",
    "content": "package io.temporal.samples.packetdelivery;\n\npublic class PacketUtils {\n  public static String COMPLETION_SUCCESS = \"Delivery Completion Successful\";\n  public static String COMPLETION_FAILURE = \"Delivery Completion Failed\";\n  public static String COMPLETION_CANCELLED = \"Delivery Completion Cancelled\";\n  public static String COMPENSATION_COMPLETED = \"Delivery Compensation Completed\";\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/README.md",
    "content": "# Async Package Delivery Sample\n\nThis sample show how to run multiple \"paths\" of execution async within single workflow.\nSample starts deliveries of 5 items in parallel. Each item performs an activity\nand then waits for a confirmation signal, then performs second activity.\n\nWorkflow waits until all packets have been delivered. Each packet delivery path can choose to\nalso \"cancel\" delivery of another item. This is done via signal and cancellation of the \nCancellationScope. \n\n## Notes\n1. In this sample we do not handle event history count and size partitioning via ContinueAsNew. It is assumed\nthat the total number of paths and path lengths (in terms of activity executions) would not exceed it. \nFor your use case you might need to add ContinueAsNew checks to deal with this situation.\n2. Use this sample as all other ones as reference for your implementation. It was not tested on high scale \nso using it as-is without load testing is not recommended.\n\n## Start the Sample:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.packetdelivery.Starter\n```\n\nRun sample multiple times to see different scenarios (delivery failure and retry and delivery cancelation)\nThere is a 10% chance delivery is going to be canceled and 20% chane it will fail. "
  },
  {
    "path": "core/src/main/java/io/temporal/samples/packetdelivery/Starter.java",
    "content": "package io.temporal.samples.packetdelivery;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowNotFoundException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.Collections;\nimport java.util.List;\n\npublic class Starter {\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(\"packet-delivery-taskqueue\");\n\n    worker.registerWorkflowImplementationTypes(PacketDeliveryWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new PacketDeliveryActivitiesImpl(client));\n\n    factory.start();\n\n    PacketDeliveryWorkflow workflow =\n        client.newWorkflowStub(\n            PacketDeliveryWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"packet-delivery-workflow\")\n                .setTaskQueue(\"packet-delivery-taskqueue\")\n                .build());\n\n    WorkflowClient.start(workflow::execute);\n\n    // start completing package deliveries (send confirmations)\n    // Query workflow for packets that need confirmation, confirm until none need confirmation any\n    // more\n    while (true) {\n      sleep(3);\n      List<Packet> packets = workflow.deliveryConfirmationPackets();\n      if (packets.isEmpty()) {\n        break;\n      }\n      // for \"fun\", reverse the list we get from delivery confirmation list\n      Collections.reverse(packets);\n\n      for (Packet p : packets) {\n        try {\n          workflow.confirmDelivery(p.getId());\n        } catch (WorkflowNotFoundException e) {\n          // In some cases with cancellations happening, workflow could be completed by now\n          // We just ignore and exit out of loop\n          break;\n        }\n      }\n    }\n\n    // wait for workflow to complete\n    String result = WorkflowStub.fromTyped(workflow).getResult(String.class);\n    System.out.println(\"** Workflow Result: \" + result);\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(seconds * 1000L);\n    } catch (Exception e) {\n      System.out.println(e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/CEWorkflow.java",
    "content": "package io.temporal.samples.payloadconverter.cloudevents;\n\nimport io.cloudevents.CloudEvent;\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CEWorkflow {\n  @WorkflowMethod\n  void exec(CloudEvent cloudEvent);\n\n  @SignalMethod\n  void addEvent(CloudEvent cloudEvent);\n\n  @QueryMethod\n  CloudEvent getLastEvent();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/CEWorkflowImpl.java",
    "content": "package io.temporal.samples.payloadconverter.cloudevents;\n\nimport io.cloudevents.CloudEvent;\nimport io.temporal.workflow.Workflow;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class CEWorkflowImpl implements CEWorkflow {\n\n  private List<CloudEvent> eventList = new ArrayList<>();\n\n  @Override\n  public void exec(CloudEvent cloudEvent) {\n\n    eventList.add(cloudEvent);\n\n    Workflow.await(() -> eventList.size() == 10);\n  }\n\n  @Override\n  public void addEvent(CloudEvent cloudEvent) {\n    eventList.add(cloudEvent);\n  }\n\n  @Override\n  public CloudEvent getLastEvent() {\n    return eventList.get(eventList.size() - 1);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/CloudEventsPayloadConverter.java",
    "content": "package io.temporal.samples.payloadconverter.cloudevents;\n\nimport com.google.protobuf.ByteString;\nimport io.cloudevents.CloudEvent;\nimport io.cloudevents.core.format.EventFormat;\nimport io.cloudevents.core.format.EventSerializationException;\nimport io.cloudevents.core.provider.EventFormatProvider;\nimport io.cloudevents.jackson.JsonFormat;\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.common.converter.DataConverterException;\nimport io.temporal.common.converter.PayloadConverter;\nimport java.lang.reflect.Type;\nimport java.nio.charset.StandardCharsets;\nimport java.util.Optional;\n\n/** Payload converter specific to CloudEvents format */\npublic class CloudEventsPayloadConverter implements PayloadConverter {\n\n  private EventFormat CEFormat =\n      EventFormatProvider.getInstance().resolveFormat(JsonFormat.CONTENT_TYPE);\n\n  @Override\n  public String getEncodingType() {\n    return \"json/plain\";\n  }\n\n  @Override\n  public Optional<Payload> toData(Object value) throws DataConverterException {\n\n    try {\n      CloudEvent cloudEvent = (CloudEvent) value;\n      byte[] serialized = CEFormat.serialize(cloudEvent);\n\n      return Optional.of(\n          Payload.newBuilder()\n              .putMetadata(\n                  \"encoding\", ByteString.copyFrom(getEncodingType(), StandardCharsets.UTF_8))\n              .setData(ByteString.copyFrom(serialized))\n              .build());\n\n    } catch (EventSerializationException | ClassCastException e) {\n      throw new DataConverterException(e);\n    }\n  }\n\n  @Override\n  public <T> T fromData(Payload content, Class<T> valueClass, Type valueType)\n      throws DataConverterException {\n    try {\n      return (T) CEFormat.deserialize(content.getData().toByteArray());\n    } catch (ClassCastException e) {\n      throw new DataConverterException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/README.md",
    "content": "# Custom Payload Converter (CloudEvents)\n\nThe sample demonstrates creating and setting a custom Payload Converter.\n\n## Running\n\n1. Start Temporal Server with \"default\" namespace enabled. \nFor example using local Docker:\n\n```bash\ngit clone https://github.com/temporalio/docker-compose.git\ncd  docker-compose\ndocker-compose up\n```\n\n2. Run the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.payloadconverter.cloudevents.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/cloudevents/Starter.java",
    "content": "package io.temporal.samples.payloadconverter.cloudevents;\n\nimport io.cloudevents.CloudEvent;\nimport io.cloudevents.core.builder.CloudEventBuilder;\nimport io.cloudevents.jackson.JsonCloudEventData;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.net.URI;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\n\npublic class Starter {\n\n  private static final String TASK_QUEUE = \"CloudEventsConverterQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    // Add CloudEventsPayloadConverter\n    // It has the same encoding type as JacksonJsonPayloadConverter\n    DefaultDataConverter ddc =\n        DefaultDataConverter.newDefaultInstance()\n            .withPayloadConverterOverrides(new CloudEventsPayloadConverter());\n\n    WorkflowClientOptions workflowClientOptions =\n        WorkflowClientOptions.newBuilder().setDataConverter(ddc).build();\n\n    WorkflowClient client = WorkflowClient.newInstance(service, workflowClientOptions);\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    worker.registerWorkflowImplementationTypes(CEWorkflowImpl.class);\n\n    factory.start();\n\n    WorkflowOptions newCustomerWorkflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build();\n\n    CEWorkflow workflow = client.newWorkflowStub(CEWorkflow.class, newCustomerWorkflowOptions);\n\n    // Create 10 cloud events\n    List<CloudEvent> cloudEventList = new ArrayList<>();\n\n    for (int i = 0; i < 10; i++) {\n      cloudEventList.add(\n          CloudEventBuilder.v1()\n              .withId(String.valueOf(100 + i))\n              .withType(\"example.demo\")\n              .withSource(URI.create(\"http://temporal.io\"))\n              .withData(\n                  \"application/json\",\n                  (\"{\\n\" + \"\\\"greeting\\\": \\\"hello \" + i + \"\\\"\\n\" + \"}\")\n                      .getBytes(Charset.defaultCharset()))\n              .build());\n    }\n\n    WorkflowClient.start(workflow::exec, cloudEventList.get(0));\n\n    // Send signals (cloud event data)\n    for (int j = 1; j < 10; j++) {\n      workflow.addEvent(cloudEventList.get(j));\n    }\n\n    // Get the CE result and get its data (JSON)\n    String result =\n        ((JsonCloudEventData) workflow.getLastEvent().getData()).getNode().get(\"greeting\").asText();\n\n    System.out.println(\"Last event body: \" + result);\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/crypto/CryptoWorkflow.java",
    "content": "package io.temporal.samples.payloadconverter.crypto;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CryptoWorkflow {\n  @WorkflowMethod\n  MyCustomer exec(MyCustomer customer);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/crypto/CryptoWorkflowImpl.java",
    "content": "package io.temporal.samples.payloadconverter.crypto;\n\npublic class CryptoWorkflowImpl implements CryptoWorkflow {\n  @Override\n  public MyCustomer exec(MyCustomer customer) {\n    // if > 18 \"approve\" otherwise deny\n    if (customer.getAge() > 18) {\n      customer.setApproved(true);\n    } else {\n      customer.setApproved(false);\n    }\n    return customer;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/crypto/MyCustomer.java",
    "content": "package io.temporal.samples.payloadconverter.crypto;\n\nimport com.codingrodent.jackson.crypto.Encrypt;\n\npublic class MyCustomer {\n  private String name;\n  private int age;\n  private boolean approved;\n\n  public MyCustomer() {}\n\n  public MyCustomer(String name, int age) {\n    this.name = name;\n    this.age = age;\n  }\n\n  @Encrypt\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  @Encrypt\n  public int getAge() {\n    return age;\n  }\n\n  public void setAge(int age) {\n    this.age = age;\n  }\n\n  @Encrypt\n  public boolean isApproved() {\n    return approved;\n  }\n\n  public void setApproved(boolean approved) {\n    this.approved = approved;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/crypto/README.md",
    "content": "# Custom Payload Converter (Crypto converter)\n\nThe sample demonstrates how you can override the default Json Converter to encrypt/decrypt payloads using [jackson-json-crypto](https://github.com/codesqueak/jackson-json-crypto).\n\n## Running\n\n1. Start Temporal Server with \"default\" namespace enabled.\n   For example using local Docker:\n\n```bash\ngit clone https://github.com/temporalio/docker-compose.git\ncd  docker-compose\ndocker-compose up\n```\n\n2. Run the following command to start the sample:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.payloadconverter.crypto.Starter\n```\n\n3. View the history in [Temporal Web UI](http://localhost:8088/)\nYou will see your workflow inputs fields that were set to be encrypted\nin the MyCustomer model class are indeed encrypted, for example:\n\n```json\n[\n  {\n    \"name\": {\n      \"salt\": \"uZnKfjmFzwYsH6ncZBVgvvmAPTw=\",\n      \"iv\": \"0nK++kg8IgtOJFs+gQ/U0A==\",\n      \"value\": \"cvXFWXfU8RFKdlWgjrHaog==\"\n    },\n    \"age\": {\n      \"salt\": \"uZnKfjmFzwYsH6ncZBVgvvmAPTw=\",\n      \"iv\": \"0nK++kg8IgtOJFs+gQ/U0A==\",\n      \"value\": \"OFA/XDiwep153xZHOECqJA==\"\n    },\n    \"approved\": {\n      \"salt\": \"uZnKfjmFzwYsH6ncZBVgvvmAPTw=\",\n      \"iv\": \"0nK++kg8IgtOJFs+gQ/U0A==\",\n      \"value\": \"Tm23RaHHKz2wM56G2Bn6Vw==\"\n    }\n  }\n]\n```\n\n   \n  "
  },
  {
    "path": "core/src/main/java/io/temporal/samples/payloadconverter/crypto/Starter.java",
    "content": "package io.temporal.samples.payloadconverter.crypto;\n\nimport com.codingrodent.jackson.crypto.CryptoModule;\nimport com.codingrodent.jackson.crypto.EncryptionService;\nimport com.codingrodent.jackson.crypto.PasswordCryptoContext;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.common.converter.JacksonJsonPayloadConverter;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class Starter {\n  private static final String TASK_QUEUE = \"CryptoConverterQueue\";\n  private static final String encryptDecryptPassword = \"encryptDecryptPassword\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    // Set crypto data converter in client options\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service,\n            WorkflowClientOptions.newBuilder()\n                .setDataConverter(\n                    DefaultDataConverter.newDefaultInstance()\n                        .withPayloadConverterOverrides(getCryptoJacksonJsonPayloadConverter()))\n                .build());\n\n    // Create worker and start Worker factory\n    createWorker(client);\n\n    // Create typed workflow stub\n    CryptoWorkflow workflow =\n        client.newWorkflowStub(\n            CryptoWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"cryptoWorkflow\")\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Start workflow exec and wait for results (sync)\n    MyCustomer customer = workflow.exec(new MyCustomer(\"John\", 22));\n\n    System.out.println(\"Approved: \" + customer.isApproved());\n\n    System.exit(0);\n  }\n\n  private static JacksonJsonPayloadConverter getCryptoJacksonJsonPayloadConverter() {\n    ObjectMapper objectMapper = new ObjectMapper();\n\n    // Create the Crypto Context (password based)\n    PasswordCryptoContext cryptoContext =\n        new PasswordCryptoContext(\n            encryptDecryptPassword, // decrypt password\n            encryptDecryptPassword, // encrypt password\n            PasswordCryptoContext.CIPHER_NAME, // cipher name\n            PasswordCryptoContext.KEY_NAME); // key generator names\n    EncryptionService encryptionService = new EncryptionService(objectMapper, cryptoContext);\n    objectMapper.registerModule(new CryptoModule().addEncryptionService(encryptionService));\n\n    return new JacksonJsonPayloadConverter(objectMapper);\n  }\n\n  private static void createWorker(WorkflowClient client) {\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(CryptoWorkflowImpl.class);\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/FailingActivities.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface FailingActivities {\n  void activityTypeA();\n\n  void activityTypeB();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/FailingActivitiesImpl.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport io.temporal.activity.Activity;\n\npublic class FailingActivitiesImpl implements FailingActivities {\n  @Override\n  public void activityTypeA() {\n    // Get the activity type\n    String type = Activity.getExecutionContext().getInfo().getActivityType();\n    // Get the retry attempt\n    int attempt = Activity.getExecutionContext().getInfo().getAttempt();\n    // Wrap checked exception and throw\n    throw Activity.wrap(\n        new NullPointerException(\"Activity type: \" + type + \" attempt times: \" + attempt));\n  }\n\n  @Override\n  public void activityTypeB() {\n    // Get the activity type\n    String type = Activity.getExecutionContext().getInfo().getActivityType();\n    // Get the retry attempt\n    int attempt = Activity.getExecutionContext().getInfo().getAttempt();\n    // Wrap checked exception and throw\n    throw Activity.wrap(\n        new NullPointerException(\"Activity type: \" + type + \" attempt times: \" + attempt));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/PerActivityOptionsWorkflow.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface PerActivityOptionsWorkflow {\n  @WorkflowMethod\n  void execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/PerActivityOptionsWorkflowImpl.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.workflow.Workflow;\nimport org.slf4j.Logger;\n\npublic class PerActivityOptionsWorkflowImpl implements PerActivityOptionsWorkflow {\n\n  // Create activity stub that will inherit activity options set in WorkflowImplementationOptions\n  // Note that you can overwrite the per-activity options by setting specific ActivityOptions\n  // when creating activity stub here (Workflow.newActivityStub(FailingActivities.class, options)\n  private FailingActivities activities = Workflow.newActivityStub(FailingActivities.class);\n\n  private Logger logger = Workflow.getLogger(this.getClass().getName());\n\n  @Override\n  public void execute() {\n\n    // Execute first activity\n    try {\n      activities.activityTypeA();\n    } catch (ActivityFailure af) {\n      // Activity invocations always throw ActivityFailure\n\n      // Our activity was retried up to its set setStartToCloseTimeout\n      // We exhausted our retries so cause of our failure is ApplicationFailure\n      // From ApplicationFailure we can get our original NPE message\n      logger.info(\"ActivityFailure cause: \" + af.getCause().getClass().getName());\n      logger.info(\"ApplicationFailure type: \" + ((ApplicationFailure) af.getCause()).getType());\n      // Original message should include a retry attempt number > 1\n      logger.info(\n          \"Application Failure orig message: \"\n              + ((ApplicationFailure) af.getCause()).getOriginalMessage());\n    }\n\n    // Execute second activity\n    try {\n      activities.activityTypeB();\n    } catch (ActivityFailure af) {\n      logger.info(\"ActivityFailure cause: \" + af.getCause().getClass().getName());\n      logger.info(\"ApplicationFailure type: \" + ((ApplicationFailure) af.getCause()).getType());\n      // Original message should include a retry attempt number == 1 since we threw the doNotRetry\n      // NPE\n      // Set in the per activity type options\n      logger.info(\n          \"Application Failure orig message: \"\n              + ((ApplicationFailure) af.getCause()).getOriginalMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/README.md",
    "content": "# Per Activity Type Options Sample\n\nThis sample shows how to set per Activity type options via\nWorkflowImplementationOptions\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.peractivityoptions.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/peractivityoptions/Starter.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport com.google.common.collect.ImmutableMap;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport java.io.IOException;\nimport java.time.Duration;\n\npublic class Starter {\n\n  public static final String TASK_QUEUE = \"perActivityTaskQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    // Create Worker\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    // Register workflow impl and set the per-activity options\n    WorkflowImplementationOptions options =\n        WorkflowImplementationOptions.newBuilder()\n            // setActivityOptions allows you to set different ActivityOption per activity type\n            // By default activity type is the name of activity method (with first letter upper\n            // cased)\n            .setActivityOptions(\n                ImmutableMap.of(\n                    \"ActivityTypeA\",\n                    ActivityOptions.newBuilder()\n                        // Set activity exec timeout (including retries)\n                        .setScheduleToCloseTimeout(Duration.ofSeconds(5))\n                        // Set activity type specific retries if needed\n                        .build(),\n                    \"ActivityTypeB\",\n                    ActivityOptions.newBuilder()\n                        // Set activity exec timeout (single run)\n                        .setStartToCloseTimeout(Duration.ofSeconds(2))\n                        .setRetryOptions(\n                            RetryOptions.newBuilder()\n                                // ActivityTypeB activity type shouldn't retry on NPE\n                                .setDoNotRetry(NullPointerException.class.getName())\n                                .build())\n                        .build()))\n            .build();\n\n    // Register our workflow impl and give the per-activity options\n    // Note you can register multiple workflow impls with worker using these activity options\n    worker.registerWorkflowImplementationTypes(options, PerActivityOptionsWorkflowImpl.class);\n\n    // Register activity impl with worker\n    worker.registerActivitiesImplementations(new FailingActivitiesImpl());\n\n    factory.start();\n\n    // Create typed workflow stub\n    PerActivityOptionsWorkflow workflow =\n        client.newWorkflowStub(\n            PerActivityOptionsWorkflow.class,\n            WorkflowOptions.newBuilder()\n                // set business level id\n                .setWorkflowId(\"PerActivityOptionsWorkflow\")\n                // set same task queue as our worker\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Call our workflow method sync (wait for results)\n    workflow.execute();\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/PollingActivities.java",
    "content": "package io.temporal.samples.polling;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface PollingActivities {\n  String doPoll();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/PollingWorkflow.java",
    "content": "package io.temporal.samples.polling;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface PollingWorkflow {\n  @WorkflowMethod\n  String exec();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/README.md",
    "content": "# Polling\n\nThese samples show three different best practices for polling.\n\n1. [Frequently Polling Activity](frequent/README.md)\n2. [Infrequently Polling Activity](infrequent/README.md)\n3. [Periodic Polling of a sequence of activities](periodicsequence/README.md)\n\nThe samples are based on [this](https://community.temporal.io/t/what-is-the-best-practice-for-a-polling-activity/328/2) community forum thread."
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/TestService.java",
    "content": "package io.temporal.samples.polling;\n\nimport java.util.concurrent.ThreadLocalRandom;\n\n/**\n * Test service that we want to poll. It simulates a service being down and then returning a result\n * after 5 attempts\n */\npublic class TestService {\n  private int tryAttempt = 0;\n  private int errorAttempts = 5; // default to 5 attempts before returning result\n  private boolean doRetryAfter = false;\n  private int minRetryAfter = 1;\n  private int maxRetryAfter = 3;\n\n  public TestService() {}\n\n  public TestService(int errorAttempts) {\n    this.errorAttempts = errorAttempts;\n  }\n\n  public TestService(int errorAttempts, boolean doRetryAfter) {\n    this.errorAttempts = errorAttempts;\n    this.doRetryAfter = doRetryAfter;\n  }\n\n  public String getServiceResult() throws TestServiceException {\n    tryAttempt++;\n    if (tryAttempt % errorAttempts == 0) {\n      return \"OK\";\n    } else {\n      if (!doRetryAfter) {\n        throw new TestServiceException(\"Service is down\");\n      } else {\n        throw new TestServiceException(\n            \"Service is down\",\n            ThreadLocalRandom.current().nextInt(minRetryAfter, maxRetryAfter + 1));\n      }\n    }\n  }\n\n  public static class TestServiceException extends Exception {\n    private int retryAfterInMinutes = 1;\n\n    public TestServiceException(String message) {\n      super(message);\n    }\n\n    public TestServiceException(String message, int retryAfterInMinutes) {\n      super(message);\n      this.retryAfterInMinutes = retryAfterInMinutes;\n    }\n\n    public int getRetryAfterInMinutes() {\n      return retryAfterInMinutes;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/frequent/FrequentPollingActivityImpl.java",
    "content": "package io.temporal.samples.polling.frequent;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.activity.ActivityExecutionContext;\nimport io.temporal.client.ActivityCompletionException;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.TestService;\nimport java.util.concurrent.TimeUnit;\n\npublic class FrequentPollingActivityImpl implements PollingActivities {\n  private final TestService service;\n  private static final int POLL_DURATION_SECONDS = 1;\n\n  public FrequentPollingActivityImpl(TestService service) {\n    this.service = service;\n  }\n\n  @Override\n  public String doPoll() {\n    ActivityExecutionContext context = Activity.getExecutionContext();\n\n    // Here we implement our polling inside the activity impl\n    while (true) {\n      try {\n        return service.getServiceResult();\n      } catch (TestService.TestServiceException e) {\n        // service \"down\" we can log\n      }\n\n      // heart beat and sleep for the poll duration\n      try {\n        context.heartbeat(null);\n      } catch (ActivityCompletionException e) {\n        // activity was either cancelled or workflow was completed or worker shut down\n        throw e;\n      }\n      sleep(POLL_DURATION_SECONDS);\n    }\n  }\n\n  private void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException e) {\n      Thread.currentThread().interrupt();\n      throw new RuntimeException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/frequent/FrequentPollingStarter.java",
    "content": "package io.temporal.samples.polling.frequent;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.samples.polling.TestService;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class FrequentPollingStarter {\n  private static WorkflowServiceStubs service;\n  private static WorkflowClient client;\n\n  static {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n  }\n\n  private static final String taskQueue = \"pollingSampleQueue\";\n  private static final String workflowId = \"FrequentPollingSampleWorkflow\";\n\n  public static void main(String[] args) {\n    // Create our worker and register workflow and activities\n    createWorker();\n\n    // Create typed workflow stub and start execution (sync, wait for results)\n    PollingWorkflow workflow =\n        client.newWorkflowStub(\n            PollingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(taskQueue).setWorkflowId(workflowId).build());\n    String result = workflow.exec();\n    System.out.println(\"Result: \" + result);\n    System.exit(0);\n  }\n\n  private static void createWorker() {\n    WorkerFactory workerFactory = WorkerFactory.newInstance(client);\n    Worker worker = workerFactory.newWorker(taskQueue);\n\n    // Register workflow and activities\n    worker.registerWorkflowImplementationTypes(FrequentPollingWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new FrequentPollingActivityImpl(new TestService()));\n\n    workerFactory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/frequent/FrequentPollingWorkflowImpl.java",
    "content": "package io.temporal.samples.polling.frequent;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class FrequentPollingWorkflowImpl implements PollingWorkflow {\n  @Override\n  public String exec() {\n    /*\n     * Frequent polling (1 second or faster) should be done inside the activity itself. Note that\n     * the activity has to heart beat on each iteration. Note that we need to set our\n     * HeartbeatTimeout in ActivityOptions shorter than the StartToClose timeout. You can use an\n     * appropriate activity retry policy for your activity.\n     */\n    ActivityOptions options =\n        ActivityOptions.newBuilder()\n            // Set activity StartToClose timeout (single activity exec), does not include retries\n            .setStartToCloseTimeout(Duration.ofSeconds(60))\n            .setHeartbeatTimeout(Duration.ofSeconds(2))\n            // For sample we just use the default retry policy (do not set explicitly)\n            .build();\n    // create our activities stub and start activity execution\n    PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);\n    return activities.doPoll();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/frequent/README.md",
    "content": "## Frequent polling\n\nThis sample shows how we can implement frequent polling (1 second or faster) inside our Activity.\nThe implementation is a loop that polls our service and then sleeps for the poll interval (1 second in the sample).\n\nTo ensure that polling activity is restarted in a timely manner, we make sure that it heartbeats on every iteration.\nNote that heartbeating only works if we set the HeartBeatTimeout to a shorter value than the activity\nStartToClose timeout.\n\n\nTo run this sample:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.polling.frequent.FrequentPollingStarter\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequent/InfrequentPollingActivityImpl.java",
    "content": "package io.temporal.samples.polling.infrequent;\n\nimport io.temporal.failure.ApplicationErrorCategory;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.TestService;\n\npublic class InfrequentPollingActivityImpl implements PollingActivities {\n  private final TestService service;\n\n  public InfrequentPollingActivityImpl(TestService service) {\n    this.service = service;\n  }\n\n  @Override\n  public String doPoll() {\n    try {\n      return service.getServiceResult();\n    } catch (TestService.TestServiceException e) {\n      // We want to rethrow the service exception so we can poll via activity retries\n      throw ApplicationFailure.newBuilder()\n          .setMessage(e.getMessage())\n          .setType(e.getClass().getName())\n          .setCause(e)\n          // This failure is expected so we set it as benign to avoid excessive logging\n          .setCategory(ApplicationErrorCategory.BENIGN)\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequent/InfrequentPollingStarter.java",
    "content": "package io.temporal.samples.polling.infrequent;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.samples.polling.TestService;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class InfrequentPollingStarter {\n  private static WorkflowServiceStubs service;\n  private static WorkflowClient client;\n\n  static {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n  }\n\n  private static final String taskQueue = \"pollingSampleQueue\";\n  private static final String workflowId = \"InfrequentPollingSampleWorkflow\";\n\n  public static void main(String[] args) {\n    // Create our worker and register workflow and activities\n    createWorker();\n\n    // Create typed workflow stub and start execution (sync, wait for results)\n    PollingWorkflow workflow =\n        client.newWorkflowStub(\n            PollingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(taskQueue).setWorkflowId(workflowId).build());\n    String result = workflow.exec();\n    System.out.println(\"Result: \" + result);\n    System.exit(0);\n  }\n\n  private static void createWorker() {\n    WorkerFactory workerFactory = WorkerFactory.newInstance(client);\n    Worker worker = workerFactory.newWorker(taskQueue);\n\n    // Register workflow and activities\n    worker.registerWorkflowImplementationTypes(InfrequentPollingWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new InfrequentPollingActivityImpl(new TestService()));\n\n    workerFactory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequent/InfrequentPollingWorkflowImpl.java",
    "content": "package io.temporal.samples.polling.infrequent;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class InfrequentPollingWorkflowImpl implements PollingWorkflow {\n  @Override\n  public String exec() {\n    /*\n     * Infrequent polling via activity can be implemented via activity retries. For this sample we\n     * want to poll the test service every 60 seconds. Here we:\n     *\n     * <ol>\n     *   <li>Set RetryPolicy backoff coefficient of 1\n     *   <li>Set initial interval to the poll frequency (since coefficient is 1, same interval will\n     *       be used for all retries)\n     * </ol>\n     *\n     * <p>With this in case our test service is \"down\" we can fail our activity and it will be\n     * retried based on our 60 second retry interval until poll is successful and we can return a\n     * result from the activity.\n     */\n    ActivityOptions options =\n        ActivityOptions.newBuilder()\n            // Set activity StartToClose timeout (single activity exec), does not include retries\n            .setStartToCloseTimeout(Duration.ofSeconds(2))\n            .setRetryOptions(\n                RetryOptions.newBuilder()\n                    .setBackoffCoefficient(1)\n                    .setInitialInterval(Duration.ofSeconds(60))\n                    .build())\n            .build();\n    // create our activities stub and start activity execution\n    PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);\n    return activities.doPoll();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequent/README.md",
    "content": "## Infrequent polling\n\nThis sample shows how we can use Activity retries for infrequent polling of a third-party service (for example via REST). \nThis method can be used for infrequent polls of one minute or slower.\n\nWe utilize activity retries for this option, setting Retries options:\n* setBackoffCoefficient to 1\n* setInitialInterval to the polling interval (in this sample set to 60 seconds)\n\nThis will allow us to retry our Activity exactly on the set interval.\n\nTo run this sample: \n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.polling.infrequent.InfrequentPollingStarter\n```\n\nSince 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. \n\nNote 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.\n\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequentwithretryafter/InfrequentPollingWithRetryAfterActivityImpl.java",
    "content": "package io.temporal.samples.polling.infrequentwithretryafter;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.failure.ApplicationErrorCategory;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.TestService;\nimport java.time.Duration;\nimport java.time.LocalDateTime;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\n\npublic class InfrequentPollingWithRetryAfterActivityImpl implements PollingActivities {\n  private final TestService service;\n  final DateTimeFormatter ISO_FORMATTER = DateTimeFormatter.ISO_DATE_TIME;\n\n  public InfrequentPollingWithRetryAfterActivityImpl(TestService service) {\n    this.service = service;\n  }\n\n  @Override\n  public String doPoll() {\n    System.out.println(\n        \"Attempt: \"\n            + Activity.getExecutionContext().getInfo().getAttempt()\n            + \" Poll time: \"\n            + LocalDateTime.now(ZoneId.systemDefault()).format(ISO_FORMATTER));\n\n    try {\n      return service.getServiceResult();\n    } catch (TestService.TestServiceException e) {\n      // we throw application failure that includes cause\n      // which is the test service exception\n      // and delay which is the interval to next retry based on test service retry-after directive\n      System.out.println(\"Activity next retry in: \" + e.getRetryAfterInMinutes() + \" minutes\");\n      throw ApplicationFailure.newBuilder()\n          .setMessage(e.getMessage())\n          .setType(e.getClass().getName())\n          .setCause(e)\n          // Here we set the next retry interval based on Retry-After duration given to us by our\n          // service\n          .setNextRetryDelay(Duration.ofMinutes(e.getRetryAfterInMinutes()))\n          // This failure is expected so we set it as benign to avoid excessive logging\n          .setCategory(ApplicationErrorCategory.BENIGN)\n          .build();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequentwithretryafter/InfrequentPollingWithRetryAfterStarter.java",
    "content": "package io.temporal.samples.polling.infrequentwithretryafter;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.samples.polling.TestService;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class InfrequentPollingWithRetryAfterStarter {\n  private static WorkflowServiceStubs service;\n  private static WorkflowClient client;\n\n  static {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n  }\n\n  private static final String taskQueue = \"pollingSampleQueue\";\n  private static final String workflowId = \"InfrequentPollingWithRetryAfterWorkflow\";\n\n  public static void main(String[] args) {\n    // Create our worker and register workflow and activities\n    createWorker();\n\n    // Create typed workflow stub and start execution (sync, wait for results)\n    PollingWorkflow workflow =\n        client.newWorkflowStub(\n            PollingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(taskQueue).setWorkflowId(workflowId).build());\n    String result = workflow.exec();\n    System.out.println(\"Result: \" + result);\n    System.exit(0);\n  }\n\n  private static void createWorker() {\n    WorkerFactory workerFactory = WorkerFactory.newInstance(client);\n    Worker worker = workerFactory.newWorker(taskQueue);\n\n    // Register workflow and activities\n    worker.registerWorkflowImplementationTypes(InfrequentPollingWithRetryAfterWorkflowImpl.class);\n    worker.registerActivitiesImplementations(\n        new InfrequentPollingWithRetryAfterActivityImpl(new TestService(4, true)));\n\n    workerFactory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequentwithretryafter/InfrequentPollingWithRetryAfterWorkflowImpl.java",
    "content": "package io.temporal.samples.polling.infrequentwithretryafter;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class InfrequentPollingWithRetryAfterWorkflowImpl implements PollingWorkflow {\n  @Override\n  public String exec() {\n    /*\n     * Infrequent polling via activity can be implemented via activity retries. For this sample we\n     * want to poll the test service initially 60 seconds. After that we want to retry it based on\n     * the Retry-After directive from the downstream servie we are invoking from the activity.\n     *\n     * <ol>\n     *   <li>Set RetryPolicy backoff coefficient of 1\n     *   <li>Set initial interval to the poll frequency (since coefficient is 1, same interval will\n     *       be used as default retry attempt)\n     * </ol>\n     */\n    ActivityOptions options =\n        ActivityOptions.newBuilder()\n            // Set activity StartToClose timeout (single activity exec), does not include retries\n            .setStartToCloseTimeout(Duration.ofSeconds(2))\n            .setRetryOptions(\n                RetryOptions.newBuilder()\n                    .setBackoffCoefficient(1)\n                    // note we don't set initial interval here\n                    .build())\n            .build();\n    // create our activities stub and start activity execution\n    PollingActivities activities = Workflow.newActivityStub(PollingActivities.class, options);\n    return activities.doPoll();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/infrequentwithretryafter/README.md",
    "content": "## Infrequent polling with Service returning Retry-After time \n\n* Note - for this sample to work you should use Temporal Service\nversion 1.24.2 or Temporal Cloud\n\n* Note - for sample we assume that our downstream service returns a retry-after duration\nthat is longer than 1 minute\n\nThis sample shows how we can use Activity retries for infrequent polling of a third-party service (for example via REST). \nThis method can be used for infrequent polls of one minute or slower.\nFor this sample our test service also returns a Retry-After time (typically its done via response header but \nfor sample its just done in service exception)\n\nWe utilize activity retries for this option, setting Retries options:\n* setBackoffCoefficient to 1\n* here we do not set initial interval as its changed by the Retry-After duration\nsent to us by the downstream service our activity invokes\n* \nThis will allow us to retry our Activity based on the Retry-After duration our downstream service \ntells us.\n\nTo run this sample: \n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.polling.infrequent.InfrequentPollingWithRetryAfterStarter\n```\n\nSince our test service simulates it being \"down\" for 3 polling attempts and then returns \"OK\" on the 4th poll attempt, \nour Workflow is going to perform 3 activity retries \nwith different intervals based on the Retry-After time our serviec gives us, \nand then return the service result on the successful 4th attempt. \n\nNote that individual Activity retries are not recorded in \nWorkflow History, so we this approach we can poll for a very long time without affecting the history size.\n\n### Sample result\nIf you run this sample you can see following in the logs for example:\n\n```\nAttempt: 1 Poll time: 2024-07-14T22:03:03.750506\nActivity next retry in: 2 minutes\nAttempt: 2 Poll time: 2024-07-14T22:05:03.780079\nActivity next retry in: 3 minutes\nAttempt: 3 Poll time: 2024-07-14T22:08:03.799703\nActivity next retry in: 1 minutes\nAttempt: 4 Poll time: 2024-07-14T22:09:03.817751\nResult: OK\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingActivityImpl.java",
    "content": "package io.temporal.samples.polling.periodicsequence;\n\nimport io.temporal.activity.Activity;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.samples.polling.TestService;\n\npublic class PeriodicPollingActivityImpl implements PollingActivities {\n\n  private TestService service;\n\n  public PeriodicPollingActivityImpl(TestService service) {\n    this.service = service;\n  }\n\n  @Override\n  public String doPoll() {\n    try {\n      return service.getServiceResult();\n    } catch (TestService.TestServiceException e) {\n      // We want to rethrow the service exception so we can poll via activity retries\n      throw Activity.wrap(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingChildWorkflowImpl.java",
    "content": "package io.temporal.samples.polling.periodicsequence;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.samples.polling.PollingActivities;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class PeriodicPollingChildWorkflowImpl implements PollingChildWorkflow {\n\n  private int singleWorkflowPollAttempts = 10;\n\n  @Override\n  public String exec(int pollingIntervalInSeconds) {\n    PollingActivities activities =\n        Workflow.newActivityStub(\n            PollingActivities.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(4))\n                // Explicitly disable default retries for activities\n                // as activity retries are handled with business logic in this case\n                .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())\n                .build());\n\n    for (int i = 0; i < singleWorkflowPollAttempts; i++) {\n      // Here we would invoke a sequence of activities\n      // For sample we just use a single one\n      try {\n        return activities.doPoll();\n      } catch (ActivityFailure e) {\n        // Log error after retries exhausted\n      }\n      // Sleep for a second\n      Workflow.sleep(Duration.ofSeconds(1));\n    }\n\n    // Request that the new child workflow run is invoked\n    PollingChildWorkflow continueAsNew = Workflow.newContinueAsNewStub(PollingChildWorkflow.class);\n    continueAsNew.exec(pollingIntervalInSeconds);\n    // unreachable\n    return null;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingStarter.java",
    "content": "package io.temporal.samples.polling.periodicsequence;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.samples.polling.TestService;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class PeriodicPollingStarter {\n  private static WorkflowServiceStubs service;\n  private static WorkflowClient client;\n\n  static {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    service = WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n  }\n\n  private static final String taskQueue = \"pollingSampleQueue\";\n  private static final String workflowId = \"PeriodicPollingSampleWorkflow\";\n\n  public static void main(String[] args) {\n    // Create our worker and register workflow and activities\n    createWorker();\n\n    // Create typed workflow stub and start execution (sync, wait for results)\n    PollingWorkflow workflow =\n        client.newWorkflowStub(\n            PollingWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(taskQueue).setWorkflowId(workflowId).build());\n    String result = workflow.exec();\n    System.out.println(\"Result: \" + result);\n    System.exit(0);\n  }\n\n  private static void createWorker() {\n    WorkerFactory workerFactory = WorkerFactory.newInstance(client);\n    Worker worker = workerFactory.newWorker(taskQueue);\n\n    // Register workflow and activities\n    worker.registerWorkflowImplementationTypes(\n        PeriodicPollingWorkflowImpl.class, PeriodicPollingChildWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new PeriodicPollingActivityImpl(new TestService(50)));\n\n    workerFactory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/PeriodicPollingWorkflowImpl.java",
    "content": "package io.temporal.samples.polling.periodicsequence;\n\nimport io.temporal.samples.polling.PollingWorkflow;\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Workflow;\n\npublic class PeriodicPollingWorkflowImpl implements PollingWorkflow {\n\n  // Set some periodic poll interval, for sample we set 5 seconds\n  private int pollingIntervalInSeconds = 5;\n\n  @Override\n  public String exec() {\n    PollingChildWorkflow childWorkflow =\n        Workflow.newChildWorkflowStub(\n            PollingChildWorkflow.class,\n            ChildWorkflowOptions.newBuilder().setWorkflowId(\"ChildWorkflowPoll\").build());\n\n    return childWorkflow.exec(pollingIntervalInSeconds);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/PollingChildWorkflow.java",
    "content": "package io.temporal.samples.polling.periodicsequence;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface PollingChildWorkflow {\n  @WorkflowMethod\n  String exec(int pollingIntervalInSeconds);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/polling/periodicsequence/README.md",
    "content": "## Periodic sequence\n\nThis samples shows periodic polling via Child Workflow. \n\nThis is a rare scenario where polling requires execution of a sequence of Activities, or Activity arguments need to change between polling retries.\n\nFor 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. \n\nThe Parent Workflow is not aware about the Child Workflow calling continue-as-new and it gets notified when it completes (or fails). \n\nTo run this sample:\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.polling.periodicsequence.PeriodicPollingStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/FailureRequester.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\n/**\n * Send signal requesting that an exception thrown from the activity is propagated to the workflow.\n */\npublic class FailureRequester {\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Note that we use the listener interface that the interceptor registered dynamically, not the\n    // workflow interface.\n    RetryOnSignalInterceptorListener workflow =\n        client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID);\n\n    // Sends \"Fail\" signal to the workflow.\n    workflow.fail();\n\n    System.out.println(\"\\\"Fail\\\" signal sent\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyActivity.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface MyActivity {\n  void execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyActivityImpl.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.failure.ApplicationFailure;\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class MyActivityImpl implements MyActivity {\n\n  /**\n   * WARNING! Never rely on such shared state in real applications. The activity variables are per\n   * process and in almost all cases multiple worker processes are used.\n   */\n  private final AtomicInteger count = new AtomicInteger();\n\n  /** Sleeps 5 seconds. Fails for 4 first invocations, and then completes. */\n  @Override\n  public void execute() {\n    try {\n      Thread.sleep(5000);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n    if (count.incrementAndGet() < 5) {\n      throw ApplicationFailure.newFailure(\"simulated\", \"type1\");\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyWorkflow.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n\n  @WorkflowMethod\n  void execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n\n  private final MyActivity activity =\n      Workflow.newActivityStub(\n          MyActivity.class,\n          ActivityOptions.newBuilder()\n              .setStartToCloseTimeout(Duration.ofSeconds(30))\n              // disable server side retries. In most production applications the retries should be\n              // done for a while before requiring an external operator signal.\n              .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())\n              .build());\n\n  @Override\n  public void execute() {\n    activity.execute();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/MyWorkflowWorker.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.io.IOException;\n\npublic class MyWorkflowWorker {\n\n  static final String TASK_QUEUE = \"RetryOnSignalInterceptor\";\n  static final String WORKFLOW_ID = \"RetryOnSignalInterceptor1\";\n\n  public static void main(String[] args) {\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    // Register interceptor with the factory.\n    WorkerFactoryOptions factoryOptions =\n        WorkerFactoryOptions.newBuilder()\n            .setWorkerInterceptors(new RetryOnSignalWorkerInterceptor())\n            .validateAndBuildWithDefaults();\n    WorkerFactory factory = WorkerFactory.newInstance(client, factoryOptions);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new MyActivityImpl());\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    MyWorkflow workflow =\n        client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Execute workflow waiting for it to complete.\n    System.out.println(\"Starting workflow \" + WORKFLOW_ID);\n    workflow.execute();\n    System.out.println(\"Workflow completed\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/QueryRequester.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class QueryRequester {\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Note that we use the listener interface that the interceptor registered dynamically, not the\n    // workflow interface.\n    RetryOnSignalInterceptorListener workflow =\n        client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID);\n\n    // Queries workflow.\n    String status = workflow.getPendingActivitiesStatus();\n\n    System.out.println(\"Workflow Pending Activities Status:\\n\\n\" + status);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/README.MD",
    "content": "# The Retry On Signal Interceptor\n\nDemonstrates an interceptor that upon activity failure waits for an external signal that indicates if activity should\nfail or retry.\n\nStarts Worker. The worker upon start initiates a workflow that has an activity that fails on the fist invocation.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker\n```\n\nSends Signal to indicate that the activity should be retried.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.RetryRequester\n```\n\nSends a signal to propagate the activity failure to the workflow instead of retrying.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.retryonsignalinterceptor.FailureRequester\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalInterceptorListener.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\n\n/** Interface used to dynamically register signal and query handlers from the interceptor. */\npublic interface RetryOnSignalInterceptorListener {\n\n  /** Requests retry of the activities waiting after failure. */\n  @SignalMethod\n  void retry();\n\n  /** Requests no more retries of the activities waiting after failure. */\n  @SignalMethod\n  void fail();\n\n  /** Returns human status of the pending activities. */\n  @QueryMethod\n  String getPendingActivitiesStatus();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalWorkerInterceptor.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.common.interceptors.*;\n\n/** Should be registered through WorkerFactoryOptions. */\npublic class RetryOnSignalWorkerInterceptor extends WorkerInterceptorBase {\n  @Override\n  public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) {\n    return new RetryOnSignalWorkflowInboundCallsInterceptor(next);\n  }\n\n  @Override\n  public ActivityInboundCallsInterceptor interceptActivity(ActivityInboundCallsInterceptor next) {\n    return next;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalWorkflowInboundCallsInterceptor.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowInboundCallsInterceptorBase;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\n\npublic class RetryOnSignalWorkflowInboundCallsInterceptor\n    extends WorkflowInboundCallsInterceptorBase {\n\n  public RetryOnSignalWorkflowInboundCallsInterceptor(WorkflowInboundCallsInterceptor next) {\n    super(next);\n  }\n\n  @Override\n  public void init(WorkflowOutboundCallsInterceptor outboundCalls) {\n    super.init(new RetryOnSignalWorkflowOutboundCallsInterceptor(outboundCalls));\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalWorkflowOutboundCallsInterceptor.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport com.google.common.base.Throwables;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptor;\nimport io.temporal.common.interceptors.WorkflowOutboundCallsInterceptorBase;\nimport io.temporal.workflow.*;\nimport java.util.ArrayList;\nimport java.util.List;\n\n/**\n * Most of the complexity of the implementation is due to the asynchronous nature of the activity\n * invocation at the interceptor level.\n */\npublic class RetryOnSignalWorkflowOutboundCallsInterceptor\n    extends WorkflowOutboundCallsInterceptorBase {\n\n  private enum Action {\n    RETRY,\n    FAIL\n  }\n\n  private class ActivityRetryState<R> {\n    private final ActivityInput<R> input;\n    private final CompletablePromise<R> asyncResult = Workflow.newPromise();\n    private CompletablePromise<Action> action;\n    private Exception lastFailure;\n    private int attempt;\n\n    private ActivityRetryState(ActivityInput<R> input) {\n      this.input = input;\n    }\n\n    ActivityOutput<R> execute() {\n      return executeWithAsyncRetry();\n    }\n\n    // Executes activity with retry based on signaled action asynchronously\n    private ActivityOutput<R> executeWithAsyncRetry() {\n      attempt++;\n      lastFailure = null;\n      action = null;\n      ActivityOutput<R> result =\n          RetryOnSignalWorkflowOutboundCallsInterceptor.super.executeActivity(input);\n      result\n          .getResult()\n          .handle(\n              (r, failure) -> {\n                // No failure complete\n                if (failure == null) {\n                  pendingActivities.remove(this);\n                  asyncResult.complete(r);\n                  return null;\n                }\n                // Asynchronously executes requested action when signal is received.\n                lastFailure = failure;\n                action = Workflow.newPromise();\n                return action.thenApply(\n                    a -> {\n                      if (a == Action.FAIL) {\n                        asyncResult.completeExceptionally(failure);\n                      } else {\n                        // Retries recursively.\n                        executeWithAsyncRetry();\n                      }\n                      return null;\n                    });\n              });\n      return new ActivityOutput<>(result.getActivityId(), asyncResult);\n    }\n\n    public void retry() {\n      if (action == null) {\n        return;\n      }\n      action.complete(Action.RETRY);\n    }\n\n    public void fail() {\n      if (action == null) {\n        return;\n      }\n      action.complete(Action.FAIL);\n    }\n\n    public String getStatus() {\n      String activityName = input.getActivityName();\n      if (lastFailure == null) {\n        return \"Executing activity \\\"\" + activityName + \"\\\". Attempt=\" + attempt;\n      }\n      if (!action.isCompleted()) {\n        return \"Last \\\"\"\n            + activityName\n            + \"\\\" activity failure:\\n\"\n            + Throwables.getStackTraceAsString(lastFailure)\n            + \"\\n\\nretry or fail ?\";\n      }\n      return (action.get() == Action.RETRY ? \"Going to retry\" : \"Going to fail\")\n          + \" activity \\\"\"\n          + activityName\n          + \"\\\"\";\n    }\n  }\n\n  /**\n   * For the example brevity the interceptor fails or retries all activities that are waiting for an\n   * action. The production version might implement retry and failure of specific activities by\n   * their type.\n   */\n  private final List<ActivityRetryState<?>> pendingActivities = new ArrayList<>();\n\n  public RetryOnSignalWorkflowOutboundCallsInterceptor(WorkflowOutboundCallsInterceptor next) {\n    super(next);\n    // Registers the listener for retry and fail signals as well as getPendingActivitiesStatus\n    // query. Register in the constructor to do it once per workflow instance.\n    Workflow.registerListener(\n        new RetryOnSignalInterceptorListener() {\n          @Override\n          public void retry() {\n            for (ActivityRetryState<?> pending : pendingActivities) {\n              pending.retry();\n            }\n          }\n\n          @Override\n          public void fail() {\n            for (ActivityRetryState<?> pending : pendingActivities) {\n              pending.fail();\n            }\n          }\n\n          @Override\n          public String getPendingActivitiesStatus() {\n            StringBuilder result = new StringBuilder();\n            for (ActivityRetryState<?> pending : pendingActivities) {\n              if (result.length() > 0) {\n                result.append('\\n');\n              }\n              result.append(pending.getStatus());\n            }\n            return result.toString();\n          }\n        });\n  }\n\n  @Override\n  public <R> ActivityOutput<R> executeActivity(ActivityInput<R> input) {\n    ActivityRetryState<R> retryState = new ActivityRetryState<R>(input);\n    pendingActivities.add(retryState);\n    return retryState.execute();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/retryonsignalinterceptor/RetryRequester.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport static io.temporal.samples.retryonsignalinterceptor.MyWorkflowWorker.WORKFLOW_ID;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class RetryRequester {\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Note that we use the listener interface that the interceptor registered dynamically, not the\n    // workflow interface.\n    RetryOnSignalInterceptorListener workflow =\n        client.newWorkflowStub(RetryOnSignalInterceptorListener.class, WORKFLOW_ID);\n\n    // Sends \"Retry\" signal to the workflow.\n    workflow.retry();\n\n    System.out.println(\"\\\"Retry\\\" signal sent\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerActivities.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport java.util.List;\nimport java.util.Set;\n\n@ActivityInterface\npublic interface ClusterManagerActivities {\n\n  class AssignNodesToJobInput {\n    private final List<String> nodes;\n    private final String jobName;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public AssignNodesToJobInput(\n        @JsonProperty(\"nodes_to_assign\") Set<String> nodesToAssign,\n        @JsonProperty(\"job_name\") String jobName) {\n      this.nodes = List.copyOf(nodesToAssign);\n      this.jobName = jobName;\n    }\n\n    @JsonProperty(\"nodes_to_assign\")\n    public List<String> getNodes() {\n      return nodes;\n    }\n\n    @JsonProperty(\"job_name\")\n    public String getJobName() {\n      return jobName;\n    }\n  }\n\n  @ActivityMethod\n  void assignNodesToJob(AssignNodesToJobInput input);\n\n  class UnassignNodesForJobInput {\n    private final List<String> nodes;\n    private final String jobName;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public UnassignNodesForJobInput(\n        @JsonProperty(\"nodes\") Set<String> nodes, @JsonProperty(\"job_name\") String jobName) {\n      this.nodes = List.copyOf(nodes);\n      this.jobName = jobName;\n    }\n\n    @JsonProperty(\"nodes\")\n    public List<String> getNodes() {\n      return nodes;\n    }\n\n    @JsonProperty(\"job_name\")\n    public String getJobName() {\n      return jobName;\n    }\n  }\n\n  @ActivityMethod\n  void unassignNodesForJob(UnassignNodesForJobInput input);\n\n  class FindBadNodesInput {\n    private final Set<String> nodesToCheck;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public FindBadNodesInput(@JsonProperty(\"assigned_nodes\") Set<String> assignedNodes) {\n      this.nodesToCheck = assignedNodes;\n    }\n\n    @JsonProperty(\"assigned_nodes\")\n    public Set<String> getNodesToCheck() {\n      return nodesToCheck;\n    }\n  }\n\n  @ActivityMethod\n  Set<String> findBadNodes(FindBadNodesInput input);\n\n  @ActivityMethod\n  void shutdown();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerActivitiesImpl.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ClusterManagerActivitiesImpl implements ClusterManagerActivities {\n  private static final Logger log = LoggerFactory.getLogger(ClusterManagerActivitiesImpl.class);\n\n  @Override\n  public void assignNodesToJob(AssignNodesToJobInput input) {\n    for (String node : input.getNodes()) {\n      log.info(\"Assigned node \" + node + \" to job \" + input.getJobName());\n    }\n    try {\n      Thread.sleep(100);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @Override\n  public void unassignNodesForJob(UnassignNodesForJobInput input) {\n    for (String node : input.getNodes()) {\n      log.info(\"Unassigned node \" + node + \" from job \" + input.getJobName());\n    }\n    try {\n      Thread.sleep(100);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @Override\n  public Set<String> findBadNodes(FindBadNodesInput input) {\n    try {\n      Thread.sleep(100);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n    Set<String> badNodes =\n        input.getNodesToCheck().stream()\n            .filter(n -> Integer.parseInt(n) % 5 == 0)\n            .collect(Collectors.toSet());\n    if (!badNodes.isEmpty()) {\n      log.info(\"Found bad nodes: \" + badNodes);\n    } else {\n      log.info(\"No bad nodes found\");\n    }\n    return badNodes;\n  }\n\n  @Override\n  public void shutdown() {\n    log.info(\"Shutting down cluster\");\n    try {\n      Thread.sleep(100);\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflow.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.util.*;\n\n/**\n * ClusterManagerWorkflow keeps track of the assignments of a cluster of nodes. Via signals, the\n * cluster can be started and shutdown. Via updates, clients can also assign jobs to nodes and\n * delete jobs. These updates must run atomically.\n */\n@WorkflowInterface\npublic interface ClusterManagerWorkflow {\n\n  enum ClusterState {\n    NOT_STARTED,\n    STARTED,\n    SHUTTING_DOWN\n  }\n\n  // In workflows that continue-as-new, it's convenient to store all your state in one serializable\n  // structure to make it easier to pass between runs\n  class ClusterManagerState {\n    public ClusterState workflowState = ClusterState.NOT_STARTED;\n    public Map<String, Optional<String>> nodes = new HashMap<>();\n    public Set<String> jobAssigned = new HashSet<>();\n  }\n\n  class ClusterManagerInput {\n    private final Optional<ClusterManagerState> state;\n    private final boolean testContinueAsNew;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ClusterManagerInput(\n        @JsonProperty(\"state\") Optional<ClusterManagerState> state,\n        @JsonProperty(\"test_continue_as_new\") boolean testContinueAsNew) {\n      this.state = state;\n      this.testContinueAsNew = testContinueAsNew;\n    }\n\n    @JsonProperty(\"state\")\n    public Optional<ClusterManagerState> getState() {\n      return state;\n    }\n\n    @JsonProperty(\"test_continue_as_new\")\n    public boolean isTestContinueAsNew() {\n      return testContinueAsNew;\n    }\n  }\n\n  class ClusterManagerResult {\n    private final int numCurrentlyAssignedNodes;\n    private final int numBadNodes;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ClusterManagerResult(\n        @JsonProperty(\"num_currently_assigned_nodes\") int numCurrentlyAssignedNodes,\n        @JsonProperty(\"num_bad_nodes\") int numBadNodes) {\n      this.numCurrentlyAssignedNodes = numCurrentlyAssignedNodes;\n      this.numBadNodes = numBadNodes;\n    }\n\n    @JsonProperty(\"num_currently_assigned_nodes\")\n    public int getNumCurrentlyAssignedNodes() {\n      return numCurrentlyAssignedNodes;\n    }\n\n    @JsonProperty(\"num_bad_nodes\")\n    public int getNumBadNodes() {\n      return numBadNodes;\n    }\n  }\n\n  // Be in the habit of storing message inputs and outputs in serializable structures.\n  // This makes it easier to add more overtime in a backward-compatible way.\n  class ClusterManagerAssignNodesToJobInput {\n    // If larger or smaller than previous amounts, will resize the job.\n    private final int totalNumNodes;\n    private final String jobName;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ClusterManagerAssignNodesToJobInput(\n        @JsonProperty(\"total_num_nodes\") int totalNumNodes,\n        @JsonProperty(\"job_name\") String jobName) {\n      this.totalNumNodes = totalNumNodes;\n      this.jobName = jobName;\n    }\n\n    @JsonProperty(\"total_num_nodes\")\n    public int getTotalNumNodes() {\n      return totalNumNodes;\n    }\n\n    @JsonProperty(\"job_name\")\n    public String getJobName() {\n      return jobName;\n    }\n  }\n\n  class ClusterManagerDeleteJobInput {\n    private final String jobName;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ClusterManagerDeleteJobInput(@JsonProperty(\"job_name\") String jobName) {\n      this.jobName = jobName;\n    }\n\n    @JsonProperty(\"job_name\")\n    public String getJobName() {\n      return jobName;\n    }\n  }\n\n  class ClusterManagerAssignNodesToJobResult {\n    private final Set<String> nodesAssigned;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public ClusterManagerAssignNodesToJobResult(\n        @JsonProperty(\"assigned_nodes\") Set<String> assignedNodes) {\n      this.nodesAssigned = assignedNodes;\n    }\n\n    @JsonProperty(\"assigned_nodes\")\n    public Set<String> getNodesAssigned() {\n      return nodesAssigned;\n    }\n  }\n\n  @WorkflowMethod\n  ClusterManagerResult run(ClusterManagerInput input);\n\n  @SignalMethod\n  void startCluster();\n\n  @UpdateMethod\n  boolean stopCluster();\n\n  // This is an update as opposed to a signal because the client may want to wait for nodes to be\n  // allocated before sending work to those nodes. Returns the list of node names that were\n  // allocated to the job.\n  @UpdateMethod\n  ClusterManagerAssignNodesToJobResult assignNodesToJobs(ClusterManagerAssignNodesToJobInput input);\n\n  // Even though it returns nothing, this is an update because the client may want to track it, for\n  // example to wait for nodes to be unassigned before reassigning them.\n  @UpdateMethod\n  void deleteJob(ClusterManagerDeleteJobInput input);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflowImpl.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInit;\nimport io.temporal.workflow.WorkflowLock;\nimport java.time.Duration;\nimport java.util.Collections;\nimport java.util.Optional;\nimport java.util.Set;\nimport java.util.stream.Collectors;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ClusterManagerWorkflowImpl implements ClusterManagerWorkflow {\n\n  private static final Logger logger = LoggerFactory.getLogger(ClusterManagerWorkflowImpl.class);\n  private final ClusterManagerState state;\n  private final WorkflowLock nodeLock;\n  private final Duration sleepInterval;\n  private final int maxHistoryLength;\n\n  private ClusterManagerActivities activities =\n      Workflow.newActivityStub(\n          ClusterManagerActivities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build(),\n          Collections.singletonMap(\n              \"findBadNodes\",\n              ActivityOptions.newBuilder()\n                  .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(1).build())\n                  .build()));\n\n  @WorkflowInit\n  public ClusterManagerWorkflowImpl(ClusterManagerInput input) {\n    nodeLock = Workflow.newWorkflowLock();\n    if (input.getState().isPresent()) {\n      state = input.getState().get();\n    } else {\n      state = new ClusterManagerState();\n    }\n    if (input.isTestContinueAsNew()) {\n      maxHistoryLength = 120;\n      sleepInterval = Duration.ofSeconds(1);\n    } else {\n      sleepInterval = Duration.ofSeconds(10);\n      maxHistoryLength = 0;\n    }\n  }\n\n  @Override\n  public ClusterManagerResult run(ClusterManagerInput input) {\n    Workflow.await(() -> state.workflowState != ClusterState.NOT_STARTED);\n    // The cluster manager is a long-running \"entity\" workflow so we need to periodically checkpoint\n    // its state and\n    // continue-as-new.\n    while (true) {\n      performHealthChecks();\n      if (!Workflow.await(\n          sleepInterval,\n          () -> state.workflowState == ClusterState.SHUTTING_DOWN || shouldContinueAsNew())) {\n      } else if (state.workflowState == ClusterState.SHUTTING_DOWN) {\n        break;\n      } else if (shouldContinueAsNew()) {\n        // We don't want to leave any job assignment or deletion handlers half-finished when we\n        // continue as new.\n        Workflow.await(() -> Workflow.isEveryHandlerFinished());\n        logger.info(\"Continuing as new\");\n        Workflow.continueAsNew(\n            new ClusterManagerInput(Optional.of(state), input.isTestContinueAsNew()));\n      }\n    }\n    // Make sure we finish off handlers such as deleting jobs before we complete the workflow.\n    Workflow.await(() -> Workflow.isEveryHandlerFinished());\n    return new ClusterManagerResult(getAssignedNodes(null).size(), getBadNodes().size());\n  }\n\n  @Override\n  public void startCluster() {\n    if (state.workflowState != ClusterState.NOT_STARTED) {\n      logger.warn(\"Cannot start cluster in state {}\", state.workflowState);\n      return;\n    }\n    state.workflowState = ClusterState.STARTED;\n    for (int i = 0; i < 25; i++) {\n      state.nodes.put(String.valueOf(i), Optional.empty());\n    }\n    logger.info(\"Cluster started\");\n  }\n\n  @Override\n  public boolean stopCluster() {\n    if (state.workflowState != ClusterState.STARTED) {\n      // This is used as an Update handler so that we can return an error to the caller.\n      throw ApplicationFailure.newFailure(\n          \"Cannot shutdown cluster in state \" + state.workflowState, \"IllegalState\");\n    }\n    activities.shutdown();\n    state.workflowState = ClusterState.SHUTTING_DOWN;\n    logger.info(\"Cluster shut down\");\n    return true;\n  }\n\n  @Override\n  public ClusterManagerAssignNodesToJobResult assignNodesToJobs(\n      ClusterManagerAssignNodesToJobInput input) {\n    Workflow.await(() -> state.workflowState != ClusterState.NOT_STARTED);\n    if (state.workflowState == ClusterState.SHUTTING_DOWN) {\n      throw ApplicationFailure.newFailure(\n          \"Cannot assign nodes to a job: Cluster is already shut down\", \"IllegalState\");\n    }\n    nodeLock.lock();\n    try {\n      // Idempotency guard.\n      if (state.jobAssigned.contains(input.getJobName())) {\n        return new ClusterManagerAssignNodesToJobResult(getAssignedNodes(input.getJobName()));\n      }\n      Set<String> unassignedNodes = getUnassignedNodes();\n      if (unassignedNodes.size() < input.getTotalNumNodes()) {\n        // If you want the client to receive a failure, either add an update validator and throw the\n        // exception from there, or raise an ApplicationFailure. Other exceptions in the main\n        // handler will cause the workflow to keep retrying and get it stuck.\n        throw ApplicationFailure.newFailure(\n            \"Cannot assign nodes to a job: Not enough nodes available\", \"IllegalState\");\n      }\n      Set<String> nodesToAssign =\n          unassignedNodes.stream().limit(input.getTotalNumNodes()).collect(Collectors.toSet());\n      // This call would be dangerous without nodesLock because it yields control and allows\n      // interleaving with deleteJob and performHealthChecks, which both touch this.state.nodes.\n      activities.assignNodesToJob(\n          new ClusterManagerActivities.AssignNodesToJobInput(nodesToAssign, input.getJobName()));\n      for (String node : nodesToAssign) {\n        state.nodes.put(node, Optional.of(input.getJobName()));\n      }\n      state.jobAssigned.add(input.getJobName());\n      return new ClusterManagerAssignNodesToJobResult(nodesToAssign);\n    } finally {\n      nodeLock.unlock();\n    }\n  }\n\n  @Override\n  public void deleteJob(ClusterManagerDeleteJobInput input) {\n    Workflow.await(() -> state.workflowState != ClusterState.NOT_STARTED);\n    if (state.workflowState == ClusterState.SHUTTING_DOWN) {\n      // If you want the client to receive a failure, either add an update validator and throw the\n      // exception from there, or raise an ApplicationFailure. Other exceptions in the main handler\n      // will cause the workflow to keep retrying and get it stuck.\n      throw ApplicationFailure.newFailure(\n          \"Cannot delete a job: Cluster is already shut down\", \"IllegalState\");\n    }\n    nodeLock.lock();\n    try {\n      Set<String> nodesToUnassign = getAssignedNodes(input.getJobName());\n      // This call would be dangerous without nodesLock because it yields control and allows\n      // interleaving\n      // with assignNodesToJob and performHealthChecks, which all touch this.state.nodes.\n      activities.unassignNodesForJob(\n          new ClusterManagerActivities.UnassignNodesForJobInput(\n              nodesToUnassign, input.getJobName()));\n      for (String node : nodesToUnassign) {\n        state.nodes.put(node, Optional.empty());\n      }\n    } finally {\n      nodeLock.unlock();\n    }\n  }\n\n  private Set<String> getAssignedNodes(String jobName) {\n    if (jobName != null) {\n      return state.nodes.entrySet().stream()\n          .filter(e -> e.getValue().isPresent() && e.getValue().get().equals(jobName))\n          .map(e -> e.getKey())\n          .collect(Collectors.toSet());\n    } else {\n      return state.nodes.entrySet().stream()\n          .filter(e -> e.getValue().isPresent() && !e.getValue().get().equals(\"BAD!\"))\n          .map(e -> e.getKey())\n          .collect(Collectors.toSet());\n    }\n  }\n\n  private Set<String> getUnassignedNodes() {\n    return state.nodes.entrySet().stream()\n        .filter(e -> !e.getValue().isPresent())\n        .map(e -> e.getKey())\n        .collect(Collectors.toSet());\n  }\n\n  private Set<String> getBadNodes() {\n    return state.nodes.entrySet().stream()\n        .filter(e -> e.getValue().isPresent() && e.getValue().get().equals(\"BAD!\"))\n        .map(e -> e.getKey())\n        .collect(Collectors.toSet());\n  }\n\n  private void performHealthChecks() {\n    nodeLock.lock();\n    try {\n      Set<String> assignedNodes = getAssignedNodes(null);\n      Set<String> badNodes =\n          activities.findBadNodes(new ClusterManagerActivities.FindBadNodesInput(assignedNodes));\n      for (String badNode : badNodes) {\n        state.nodes.put(badNode, Optional.of(\"BAD!\"));\n      }\n    } catch (Exception e) {\n      logger.error(\"Health check failed\", e);\n    } finally {\n      nodeLock.unlock();\n    }\n  }\n\n  private boolean shouldContinueAsNew() {\n    if (Workflow.getInfo().isContinueAsNewSuggested()) {\n      return true;\n    }\n    // This is just for ease-of-testing.  In production, we trust temporal to tell us when to\n    // continue as new.\n    if (maxHistoryLength > 0 && Workflow.getInfo().getHistoryLength() > maxHistoryLength) {\n      return true;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflowStarter.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport static io.temporal.samples.safemessagepassing.ClusterManagerWorkflowWorker.CLUSTER_MANAGER_WORKFLOW_ID;\nimport static io.temporal.samples.safemessagepassing.ClusterManagerWorkflowWorker.TASK_QUEUE;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.client.WorkflowUpdateStage;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.UUID;\nimport java.util.concurrent.CompletableFuture;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ClusterManagerWorkflowStarter {\n\n  private static final Logger logger = LoggerFactory.getLogger(ClusterManagerWorkflowStarter.class);\n\n  public static void main(String[] args) {\n    if (args.length > 1) {\n      System.err.println(\n          \"Usage: java \"\n              + ClusterManagerWorkflowStarter.class.getName()\n              + \" <test continue as new>\");\n      System.exit(1);\n    }\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    boolean shouldTestContinueAsNew = args.length > 0 ? Boolean.parseBoolean(args[0]) : false;\n    ClusterManagerWorkflow cluster =\n        client.newWorkflowStub(\n            ClusterManagerWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(TASK_QUEUE)\n                .setWorkflowId(CLUSTER_MANAGER_WORKFLOW_ID + \"-\" + UUID.randomUUID())\n                .build());\n\n    logger.info(\"Starting cluster\");\n    WorkflowClient.start(\n        cluster::run,\n        new ClusterManagerWorkflow.ClusterManagerInput(Optional.empty(), shouldTestContinueAsNew));\n    Duration delay = shouldTestContinueAsNew ? Duration.ofSeconds(10) : Duration.ofSeconds(1);\n    cluster.startCluster();\n    logger.info(\"Assigning jobs to nodes...\");\n    List<CompletableFuture<ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult>>\n        assignJobs = new ArrayList<>();\n    for (int i = 0; i < 6; i++) {\n      assignJobs.add(\n          WorkflowStub.fromTyped(cluster)\n              .startUpdate(\n                  \"assignNodesToJobs\",\n                  WorkflowUpdateStage.ACCEPTED,\n                  ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult.class,\n                  new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(2, \"job\" + i))\n              .getResultAsync());\n    }\n    assignJobs.forEach(CompletableFuture::join);\n\n    logger.info(\"Sleeping for \" + delay.getSeconds() + \" seconds\");\n    try {\n      Thread.sleep(delay.toMillis());\n    } catch (InterruptedException e) {\n      throw new RuntimeException(e);\n    }\n    logger.info(\"Deleting jobs...\");\n    List<CompletableFuture<Void>> deleteJobs = new ArrayList<>();\n    for (int i = 0; i < 6; i++) {\n      deleteJobs.add(\n          WorkflowStub.fromTyped(cluster)\n              .startUpdate(\n                  \"deleteJob\",\n                  WorkflowUpdateStage.ACCEPTED,\n                  Void.class,\n                  new ClusterManagerWorkflow.ClusterManagerDeleteJobInput(\"job\" + i))\n              .getResultAsync());\n    }\n    deleteJobs.forEach(CompletableFuture::join);\n\n    logger.info(\"Stopping cluster...\");\n    cluster.stopCluster();\n\n    ClusterManagerWorkflow.ClusterManagerResult result =\n        cluster.run(new ClusterManagerWorkflow.ClusterManagerInput(Optional.empty(), false));\n    logger.info(\n        \"Cluster shut down successfully.  It had \"\n            + result.getNumCurrentlyAssignedNodes()\n            + \" nodes assigned at the end.\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflowWorker.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ClusterManagerWorkflowWorker {\n  private static final Logger logger = LoggerFactory.getLogger(ClusterManagerWorkflowWorker.class);\n  static final String TASK_QUEUE = \"ClusterManagerWorkflowTaskQueue\";\n  static final String CLUSTER_MANAGER_WORKFLOW_ID = \"ClusterManagerWorkflow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    final Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(ClusterManagerWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new ClusterManagerActivitiesImpl());\n    factory.start();\n    logger.info(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/safemessagepassing/README.md",
    "content": "# Safe Message Passing\n\nThis sample shows off important techniques for handling signals and updates, aka messages.  In particular, it illustrates how message handlers can interleave or not be completed before the workflow completes, and how you can manage that.\n\n* Here, using Workflow.await, signal and update handlers will only operate when the workflow is within a certain state--between clusterStarted and clusterShutdown.\n* You can run start_workflow with an initializer signal that you want to run before anything else other than the workflow's constructor.  This pattern is known as \"signal-with-start.\"\n* Message handlers can block and their actions can be interleaved with one another and with the main workflow.  This can easily cause bugs, so you can use a lock to protect shared state from interleaved access.\n* An \"Entity\" workflow, i.e. a long-lived workflow, periodically \"continues as new\".  It must do this to prevent its history from growing too large, and it passes its state to the next workflow.  You can check `Workflow.getInfo().isContinueAsNewSuggested()` to see when it's time.\n* Most people want their message handlers to finish before the workflow run completes or continues as new.  Use `Workflow.await(() -> Workflow.isEveryHandlerFinished())` to achieve this.\n* Message handlers can be made idempotent.  See update `ClusterManagerWorkflow.assignNodesToJobs`.\n\nFirst start the Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.safemessagepassing.ClusterManagerWorkflowWorker\n```\n\nThen in a different terminal window start the Workflow Execution:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.safemessagepassing.ClusterManagerWorkflowStarter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/README.md",
    "content": "# Sleep for days\n\nThis sample demonstrates how to use Temporal to run a workflow that periodically sleeps for a number of days.\n\n## Run the sample\n\n1. Start the Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Worker\n```\n\n2. Start the Starter\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.sleepfordays.Starter\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivity.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface SendEmailActivity {\n  void sendEmail(String email);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/SendEmailActivityImpl.java",
    "content": "package io.temporal.samples.sleepfordays;\n\npublic class SendEmailActivityImpl implements SendEmailActivity {\n  @Override\n  public void sendEmail(String email) {\n    System.out.println(email);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysImpl.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Promise;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class SleepForDaysImpl implements SleepForDaysWorkflow {\n\n  private final SendEmailActivity activity;\n  private boolean complete = false;\n\n  public SleepForDaysImpl() {\n    this.activity =\n        Workflow.newActivityStub(\n            SendEmailActivity.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n  }\n\n  @Override\n  public String sleepForDays() {\n    while (!this.complete) {\n      activity.sendEmail(String.format(\"Sleeping for 30 days\"));\n      Promise<Void> timer = Workflow.newTimer(Duration.ofDays(30));\n      Workflow.await(() -> timer.isCompleted() || this.complete);\n    }\n\n    return \"done!\";\n  }\n\n  @Override\n  public void complete() {\n    this.complete = true;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/SleepForDaysWorkflow.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface SleepForDaysWorkflow {\n  @WorkflowMethod\n  String sleepForDays();\n\n  @SignalMethod\n  void complete();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/Starter.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class Starter {\n\n  public static final String TASK_QUEUE = \"SleepForDaysTaskQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Start a workflow execution.\n    SleepForDaysWorkflow workflow =\n        client.newWorkflowStub(\n            SleepForDaysWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(TASK_QUEUE).build());\n\n    // Start the workflow.\n    WorkflowClient.start(workflow::sleepForDays);\n\n    WorkflowStub stub = WorkflowStub.fromTyped(workflow);\n\n    // Wait for workflow to complete. This will wait indefinitely until a 'complete' signal is sent.\n    stub.getResult(String.class);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/sleepfordays/Worker.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\npublic class Worker {\n  public static final String TASK_QUEUE = \"SleepForDaysTaskQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    io.temporal.worker.Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(SleepForDaysImpl.class);\n    worker.registerActivitiesImplementations(new SendEmailActivityImpl());\n\n    factory.start();\n    System.out.println(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/ssl/MyWorkflow.java",
    "content": "package io.temporal.samples.ssl;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/ssl/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.ssl;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n  @Override\n  public String execute() {\n    return \"done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/ssl/README.md",
    "content": "# Workflow execution with mTLS\n\nThis example shows how to secure your Temporal application with [mTLS](https://docs.temporal.io/security/#encryption-in-transit-with-mtls).\nThis is required to connect with Temporal Cloud or any production Temporal deployment.\n\n\n## Export env variables\n\nBefore running the example you need to export the following env variables: \n\n- TEMPORAL_ENDPOINT: grpc endpoint, for Temporal Cloud would like `${namespace}.tmprl.cloud:7233`.\n- TEMPORAL_NAMESPACE: Namespace.\n- TEMPORAL_CLIENT_CERT: For Temporal Cloud see requirements [here](https://docs.temporal.io/cloud/how-to-manage-certificates-in-temporal-cloud#end-entity-certificates).\n- TEMPORAL_CLIENT_KEY: For Temporal Cloud see requirements [here](https://docs.temporal.io/cloud/how-to-manage-certificates-in-temporal-cloud#end-entity-certificates).\n\n## Running this sample\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.ssl.Starter\n```\n\n## Refreshing credentials\n\n- TEMPORAL_CREDENTIAL_REFRESH_PERIOD: The period in seconds to refresh the credentials in minutes.\n\nSetting this env variable will cause the worker to periodically update its credentials. For the full documentation see [here](https://grpc.github.io/grpc-java/javadoc/io/grpc/util/AdvancedTlsX509KeyManager.html).\n\n# Workflow execution with mTLS and custom Certificate Authority\n\nThis sample shows how to start a worker that connects to a temporal cluster with mTLS enabled; created by ([tls-simple sample](https://github.com/temporalio/samples-server/tree/main/tls/tls-simple));\n\nSslEnabledWorkerCustomCA demonstrates:\n\n- Passing a custom CA certificate file as parameter\n- Overriding the authority name used for TLS handshakes (if needed)\n\nThis can be useful when connecting to Temporal Cloud through [AWS Privatelink](https://docs.temporal.io/cloud/security#privatelink)\n\n1.Start a temporal cluster with tls\n\nPlease follow the temporal server-sample to start simple Temporal mTLS cluster locally: [tls-simple](https://github.com/temporalio/samples-server/tree/main/tls/tls-simple)\n\n2.Set environment variables\n\n```bash\n# Environment variables\n# paths to ca cert, client cert and client key come from the previous step \nexport TEMPORAL_CLIENT_CERT=\"</path/to/client.pem>\"\nexport TEMPORAL_CLIENT_KEY=\"</path/to/client.key>\"\nexport TEMPORAL_CA_CERT=\"</path/to/ca.cert>\"    \nexport TEMPORAL_ENDPOINT=\"localhost:7233\"    # Temporal grpc endpoint       \nexport TEMPORAL_NAMESPACE=\"default\"          # Temporal namespace            \nexport TEMPORAL_SERVER_HOSTNAME=\"tls-sample\" # Temporal server host name  \n```\n\n3.Start the Worker\n\n```bash\n./gradlew -q execute -PmainClass=\"io.temporal.samples.ssl.SslEnabledWorkerCustomCA\"\n```\n\n4.Expected result\n\n```text\n[main] INFO  i.t.s.WorkflowServiceStubsImpl - Created WorkflowServiceStubs for channel: ManagedChannelOrphanWrapper{delegate=ManagedChannelImpl{logId=1, target=localhost:7233}} \n[main] INFO  io.temporal.internal.worker.Poller - start: Poller{name=Workflow Poller taskQueue=\"MyTaskQueue\", namespace=\"default\"} \nWorkflow completed:done \n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/ssl/SslEnabledWorkerCustomCA.java",
    "content": "package io.temporal.samples.ssl;\n\nimport io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.FileInputStream;\nimport java.io.InputStream;\n\npublic class SslEnabledWorkerCustomCA {\n\n  static final String TASK_QUEUE = \"MyTaskQueue\";\n\n  public static void main(String[] args) throws Exception {\n\n    // Load your client certificate, which should look like:\n    // -----BEGIN CERTIFICATE-----\n    // ...\n    // -----END CERTIFICATE-----\n    InputStream clientCert = new FileInputStream(System.getenv(\"TEMPORAL_CLIENT_CERT\"));\n\n    // PKCS8 client key, which should look like:\n    // -----BEGIN PRIVATE KEY-----\n    // ...\n    // -----END PRIVATE KEY-----\n    InputStream clientKey = new FileInputStream(System.getenv(\"TEMPORAL_CLIENT_KEY\"));\n\n    // Load your Certification Authority certificate, which should look like:\n    // -----BEGIN CERTIFICATE-----\n    // ...\n    // -----END CERTIFICATE-----\n    InputStream caCert = new FileInputStream(System.getenv(\"TEMPORAL_CA_CERT\"));\n\n    // For temporal cloud this would likely be ${namespace}.tmprl.cloud:7233\n    String targetEndpoint = System.getenv(\"TEMPORAL_ENDPOINT\");\n\n    // Your registered namespace.\n    String namespace = System.getenv(\"TEMPORAL_NAMESPACE\");\n\n    // Create an SSL Context using the client certificate and key based on the implementation\n    // SimpleSslContextBuilder\n    // https://github.com/temporalio/sdk-java/blob/master/temporal-serviceclient/src/main/java/io/temporal/serviceclient/SimpleSslContextBuilder.java\n    SslContext sslContext =\n        GrpcSslContexts.configure(\n                SslContextBuilder.forClient()\n                    .keyManager(clientCert, clientKey)\n                    .trustManager(caCert))\n            .build();\n\n    // Create SSL enabled client by passing SslContext, created by\n    // SimpleSslContextBuilder.\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(\n            WorkflowServiceStubsOptions.newBuilder()\n                .setSslContext(sslContext)\n                .setTarget(targetEndpoint)\n                // Override the authority name used for TLS handshakes\n                .setChannelInitializer(\n                    c -> c.overrideAuthority(System.getenv(\"TEMPORAL_SERVER_HOSTNAME\")))\n                .build());\n\n    // Now setup and start workflow worker, which uses SSL enabled gRPC service to\n    // communicate with backend. client that can be used to start and signal workflows.\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service, WorkflowClientOptions.newBuilder().setNamespace(namespace).build());\n\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    MyWorkflow workflow =\n        client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(\"WORKFLOW_ID\")\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our execute method is\n     * synchronous.\n     */\n    String greeting = workflow.execute();\n\n    // Display workflow execution results\n    System.out.println(\"Workflow completed:\" + greeting);\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/ssl/Starter.java",
    "content": "package io.temporal.samples.ssl;\n\nimport io.grpc.netty.shaded.io.grpc.netty.GrpcSslContexts;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.SslContext;\nimport io.grpc.netty.shaded.io.netty.handler.ssl.SslContextBuilder;\nimport io.grpc.util.AdvancedTlsX509KeyManager;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.serviceclient.SimpleSslContextBuilder;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.File;\nimport java.io.FileInputStream;\nimport java.util.concurrent.Executors;\nimport java.util.concurrent.TimeUnit;\n\npublic class Starter {\n\n  static final String TASK_QUEUE = \"MyTaskQueue\";\n  static final String WORKFLOW_ID = \"HelloSSLWorkflow\";\n\n  public static void main(String[] args) throws Exception {\n    // Load your client certificate, which should look like:\n    // -----BEGIN CERTIFICATE-----\n    // ...\n    // -----END CERTIFICATE-----\n    File clientCertFile = new File(System.getenv(\"TEMPORAL_CLIENT_CERT\"));\n    // PKCS8 client key, which should look like:\n    // -----BEGIN PRIVATE KEY-----\n    // ...\n    // -----END PRIVATE KEY-----\n    File clientKeyFile = new File(System.getenv(\"TEMPORAL_CLIENT_KEY\"));\n    // For temporal cloud this would likely be ${namespace}.tmprl.cloud:7233\n    String targetEndpoint = System.getenv(\"TEMPORAL_ENDPOINT\");\n    // Your registered namespace.\n    String namespace = System.getenv(\"TEMPORAL_NAMESPACE\");\n    // How often to refresh the client key and certificate\n    String refreshPeriodString = System.getenv(\"TEMPORAL_CREDENTIAL_REFRESH_PERIOD\");\n    long refreshPeriod = refreshPeriodString != null ? Integer.parseInt(refreshPeriodString) : 0;\n    // Create SSL context from SimpleSslContextBuilder\n    SslContext sslContext =\n        SimpleSslContextBuilder.forPKCS8(\n                new FileInputStream(clientCertFile), new FileInputStream(clientKeyFile))\n            .build();\n    // To refresh the client key and certificate, create an AdvancedTlsX509KeyManager and manually\n    // configure the SSL context.\n    if (refreshPeriod > 0) {\n      AdvancedTlsX509KeyManager clientKeyManager = new AdvancedTlsX509KeyManager();\n      // Reload credentials every minute\n      clientKeyManager.updateIdentityCredentials(\n          clientCertFile,\n          clientKeyFile,\n          refreshPeriod,\n          TimeUnit.MINUTES,\n          Executors.newScheduledThreadPool(1));\n      sslContext =\n          GrpcSslContexts.configure(SslContextBuilder.forClient().keyManager(clientKeyManager))\n              .build();\n    }\n\n    // Create SSL enabled client by passing SslContext\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(\n            WorkflowServiceStubsOptions.newBuilder()\n                .setSslContext(sslContext)\n                .setTarget(targetEndpoint)\n                .build());\n\n    // Now setup and start workflow worker, which uses SSL enabled gRPC service to communicate with\n    // backend.\n    // client that can be used to start and signal workflows.\n    WorkflowClient client =\n        WorkflowClient.newInstance(\n            service, WorkflowClientOptions.newBuilder().setNamespace(namespace).build());\n    // worker factory that can be used to create workers for specific task queues\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    /*\n     * Define the workflow worker. Workflow workers listen to a defined task queue and process\n     * workflows and activities.\n     */\n    Worker worker = factory.newWorker(TASK_QUEUE);\n\n    /*\n     * Register our workflow implementation with the worker.\n     * Workflow implementations must be known to the worker at runtime in\n     * order to dispatch workflow tasks.\n     */\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    /*\n     * Register our Activity Types with the Worker. Since Activities are stateless and thread-safe,\n     * the Activity Type is a shared instance.\n     */\n    // worker.registerActivitiesImplementations(...);\n\n    /*\n     * Start all the workers registered for a specific task queue.\n     * The started workers then start polling for workflows and activities.\n     */\n    factory.start();\n\n    // Create the workflow client stub. It is used to start our workflow execution.\n    MyWorkflow workflow =\n        client.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(WORKFLOW_ID)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    /*\n     * Execute our workflow and wait for it to complete. The call to our execute method is\n     * synchronous.\n     */\n    String greeting = workflow.execute();\n\n    // Display workflow execution results\n    System.out.println(greeting);\n    // System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/CountActivities.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport static io.temporal.samples.standaloneactivities.StandaloneActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.ActivityClient;\nimport io.temporal.client.ActivityClientOptions;\nimport io.temporal.client.ActivityExecutionCount;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\n/** Counts standalone activity executions on the task queue. */\npublic class CountActivities {\n\n  public static void main(String[] args) throws IOException {\n    ClientConfigProfile profile = ClientConfigProfile.load();\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    ActivityClient client =\n        ActivityClient.newInstance(\n            service,\n            ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());\n\n    try {\n      ActivityExecutionCount resp = client.countExecutions(\"TaskQueue = '\" + TASK_QUEUE + \"'\");\n\n      System.out.println(\"Total activities: \" + resp.getCount());\n      resp.getGroups()\n          .forEach(\n              group ->\n                  System.out.println(\"Group \" + group.getGroupValues() + \": \" + group.getCount()));\n    } finally {\n      service.shutdown();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/ExecuteActivity.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport static io.temporal.samples.standaloneactivities.StandaloneActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.ActivityClient;\nimport io.temporal.client.ActivityClientOptions;\nimport io.temporal.client.StartActivityOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Executes a standalone activity and waits for the result. Requires a Worker running\n * StandaloneActivityWorker.\n */\npublic class ExecuteActivity {\n\n  static final String ACTIVITY_ID = \"standalone-activity-id\";\n\n  public static void main(String[] args) throws IOException {\n    ClientConfigProfile profile = ClientConfigProfile.load();\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    ActivityClient client =\n        ActivityClient.newInstance(\n            service,\n            ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());\n\n    StartActivityOptions options =\n        StartActivityOptions.newBuilder()\n            .setId(ACTIVITY_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setStartToCloseTimeout(Duration.ofSeconds(10))\n            .build();\n\n    try {\n      String result =\n          client.execute(\n              GreetingActivities.class,\n              GreetingActivities::composeGreeting,\n              options,\n              \"Hello\",\n              \"World\");\n      System.out.println(\"Activity result: \" + result);\n    } finally {\n      service.shutdown();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/GreetingActivities.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\n\n/** Activity interface shared by all programs in this sample. */\n@ActivityInterface\npublic interface GreetingActivities {\n\n  @ActivityMethod\n  String composeGreeting(String greeting, String name);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/GreetingActivitiesImpl.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\n/** Activity implementation. */\npublic class GreetingActivitiesImpl implements GreetingActivities {\n\n  private static final Logger log = LoggerFactory.getLogger(GreetingActivitiesImpl.class);\n\n  @Override\n  public String composeGreeting(String greeting, String name) {\n    log.info(\"Composing greeting...\");\n    return greeting + \", \" + name + \"!\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/ListActivities.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport static io.temporal.samples.standaloneactivities.StandaloneActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.ActivityClient;\nimport io.temporal.client.ActivityClientOptions;\nimport io.temporal.client.ActivityExecutionMetadata;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.util.stream.Stream;\n\n/** Lists standalone activity executions on the task queue. */\npublic class ListActivities {\n\n  public static void main(String[] args) throws IOException {\n    ClientConfigProfile profile = ClientConfigProfile.load();\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    ActivityClient client =\n        ActivityClient.newInstance(\n            service,\n            ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());\n\n    try (Stream<ActivityExecutionMetadata> activities =\n        client.listExecutions(\"TaskQueue = '\" + TASK_QUEUE + \"'\")) {\n      activities.forEach(\n          info ->\n              System.out.printf(\n                  \"ActivityID: %s, Type: %s, Status: %s%n\",\n                  info.getActivityId(), info.getActivityType(), info.getStatus()));\n    } finally {\n      service.shutdown();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/README.md",
    "content": "# Standalone Activities\n\nThis sample demonstrates [Standalone Activities](https://docs.temporal.io/develop/java/activities/standalone-activities),\nwhich run independently without being orchestrated by a Workflow. You start and manage them directly\nfrom a Temporal Client using `ActivityClient`.\n\nThe sample has these separate programs:\n\n| Program | Purpose |\n|---|---|\n| `StandaloneActivityWorker` | Runs a Worker that processes activity tasks |\n| `ExecuteActivity` | Starts an activity and waits for its result |\n| `StartActivity` | Starts an activity without blocking, then waits for the result |\n| `ListActivities` | Lists activity executions on the task queue |\n| `CountActivities` | Counts activity executions on the task queue |\n\n## Prerequisites\n\n- Temporal dev server with Standalone Activity support. See the\n  [Java SDK Standalone Activities guide](https://docs.temporal.io/develop/java/activities/standalone-activities#get-started)\n  for download instructions.\n\n## Start the Temporal development server\n\n```bash\n./temporal server start-dev\n```\n\n## Run the Worker\n\nIn a terminal, start the Worker. Leave it running to process activities.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.standaloneactivities.StandaloneActivityWorker\n```\n\n## Execute a Standalone Activity\n\nIn another terminal, execute an activity and wait for its result:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.standaloneactivities.ExecuteActivity\n```\n\nOr use the Temporal CLI:\n\n```bash\n./temporal activity execute \\\n  --type ComposeGreeting \\\n  --activity-id standalone-activity-id \\\n  --task-queue standalone-activity-task-queue \\\n  --start-to-close-timeout 10s \\\n  --input '\"Hello\"' \\\n  --input '\"World\"'\n```\n\n## Start a Standalone Activity without waiting\n\nStart an activity and retrieve its result separately:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.standaloneactivities.StartActivity\n```\n\nOr use the Temporal CLI:\n\n```bash\n./temporal activity start \\\n  --type ComposeGreeting \\\n  --activity-id standalone-activity-id \\\n  --task-queue standalone-activity-task-queue \\\n  --start-to-close-timeout 10s \\\n  --input '\"Hello\"' \\\n  --input '\"World\"'\n```\n\n## List Standalone Activities\n\nList activity executions on the task queue:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.standaloneactivities.ListActivities\n```\n\nOr use the Temporal CLI:\n\n```bash\n./temporal activity list\n```\n\n## Count Standalone Activities\n\nCount activity executions on the task queue:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.standaloneactivities.CountActivities\n```\n\nOr use the Temporal CLI:\n\n```bash\n./temporal activity count\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/StandaloneActivityWorker.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\n\n/**\n * Worker that processes standalone activity tasks. Run this before executing or starting standalone\n * activities with ExecuteActivity or StartActivity.\n */\npublic class StandaloneActivityWorker {\n\n  static final String TASK_QUEUE = \"standalone-activity-task-queue\";\n\n  public static void main(String[] args) throws IOException {\n    ClientConfigProfile profile = ClientConfigProfile.load();\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n\n    factory.start();\n    System.out.println(\"Worker running on task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/standaloneactivities/StartActivity.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport static io.temporal.samples.standaloneactivities.StandaloneActivityWorker.TASK_QUEUE;\n\nimport io.temporal.client.ActivityClient;\nimport io.temporal.client.ActivityClientOptions;\nimport io.temporal.client.ActivityHandle;\nimport io.temporal.client.StartActivityOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.time.Duration;\n\n/**\n * Starts a standalone activity without blocking, then waits for the result using the returned\n * handle. Requires a Worker running StandaloneActivityWorker.\n */\npublic class StartActivity {\n\n  static final String ACTIVITY_ID = \"standalone-activity-id\";\n\n  public static void main(String[] args) throws IOException {\n    ClientConfigProfile profile = ClientConfigProfile.load();\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    ActivityClient client =\n        ActivityClient.newInstance(\n            service,\n            ActivityClientOptions.newBuilder().setNamespace(profile.getNamespace()).build());\n\n    StartActivityOptions options =\n        StartActivityOptions.newBuilder()\n            .setId(ACTIVITY_ID)\n            .setTaskQueue(TASK_QUEUE)\n            .setStartToCloseTimeout(Duration.ofSeconds(10))\n            .build();\n\n    try {\n      ActivityHandle<String> handle =\n          client.start(\n              GreetingActivities.class,\n              GreetingActivities::composeGreeting,\n              options,\n              \"Hello\",\n              \"World\");\n      System.out.println(\"Started activity ID: \" + ACTIVITY_ID);\n\n      String result = handle.getResult();\n      System.out.println(\"Activity result: \" + result);\n    } finally {\n      service.shutdown();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/terminateworkflow/MyWorkflow.java",
    "content": "package io.temporal.samples.terminateworkflow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MyWorkflow {\n  @WorkflowMethod\n  String execute();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/terminateworkflow/MyWorkflowImpl.java",
    "content": "package io.temporal.samples.terminateworkflow;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class MyWorkflowImpl implements MyWorkflow {\n  @Override\n  public String execute() {\n    // This workflow just sleeps\n    Workflow.sleep(Duration.ofSeconds(20));\n    return \"done\";\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/terminateworkflow/README.md",
    "content": "# Terminate Workflow execution\n\nThe sample demonstrates how to terminate Workflow Execution using the Client API.\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.terminateworkflow.Starter\n```\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/terminateworkflow/Starter.java",
    "content": "package io.temporal.samples.terminateworkflow;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport java.util.concurrent.TimeUnit;\n\npublic class Starter {\n\n  public static final String TASK_QUEUE = \"terminateQueue\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n\n    // Create Worker\n    createWorker(factory);\n\n    // Create Workflow options\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(\"toTerminateWorkflow\")\n            .setTaskQueue(TASK_QUEUE)\n            .build();\n\n    // Get the Workflow stub\n    MyWorkflow myWorkflowStub = client.newWorkflowStub(MyWorkflow.class, workflowOptions);\n\n    // Start workflow async\n    WorkflowExecution execution = WorkflowClient.start(myWorkflowStub::execute);\n\n    // Let it run for a couple of seconds\n    sleepSeconds(2);\n\n    // Terminate it\n    WorkflowStub untyped = WorkflowStub.fromTyped(myWorkflowStub);\n    untyped.terminate(\"Sample reason\");\n\n    // Check workflow status, should be WORKFLOW_EXECUTION_STATUS_TERMINATED\n    System.out.println(\"Status: \" + getStatusAsString(execution, client, service));\n\n    System.exit(0);\n  }\n\n  /** This method creates a Worker from the factory. */\n  private static void createWorker(WorkerFactory factory) {\n    Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(MyWorkflowImpl.class);\n\n    factory.start();\n  }\n\n  /**\n   * Convenience method to sleep for a number of seconds.\n   *\n   * @param seconds amount of seconds to sleep\n   */\n  private static void sleepSeconds(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (Exception e) {\n      System.out.println(\"Exception: \" + e.getMessage());\n      System.exit(0);\n    }\n  }\n\n  /**\n   * This method uses DescribeWorkflowExecutionRequest to get the status of a workflow given a\n   * WorkflowExecution.\n   *\n   * @param execution workflow execution\n   * @return Workflow status\n   */\n  private static String getStatusAsString(\n      WorkflowExecution execution, WorkflowClient client, WorkflowServiceStubs service) {\n    DescribeWorkflowExecutionRequest describeWorkflowExecutionRequest =\n        DescribeWorkflowExecutionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setExecution(execution)\n            .build();\n\n    DescribeWorkflowExecutionResponse resp =\n        service.blockingStub().describeWorkflowExecution(describeWorkflowExecutionRequest);\n\n    WorkflowExecutionInfo workflowExecutionInfo = resp.getWorkflowExecutionInfo();\n    return workflowExecutionInfo.getStatus().toString();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/JaegerUtils.java",
    "content": "package io.temporal.samples.tracing;\n\nimport io.jaegertracing.internal.JaegerTracer;\nimport io.jaegertracing.internal.reporters.RemoteReporter;\nimport io.jaegertracing.internal.samplers.ConstSampler;\nimport io.jaegertracing.spi.Sampler;\nimport io.jaegertracing.thrift.internal.senders.UdpSender;\nimport io.opentelemetry.api.common.Attributes;\nimport io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator;\nimport io.opentelemetry.context.propagation.ContextPropagators;\nimport io.opentelemetry.context.propagation.TextMapPropagator;\nimport io.opentelemetry.exporter.jaeger.JaegerGrpcSpanExporter;\nimport io.opentelemetry.extension.trace.propagation.JaegerPropagator;\nimport io.opentelemetry.opentracingshim.OpenTracingShim;\nimport io.opentelemetry.sdk.OpenTelemetrySdk;\nimport io.opentelemetry.sdk.resources.Resource;\nimport io.opentelemetry.sdk.trace.SdkTracerProvider;\nimport io.opentelemetry.sdk.trace.export.SimpleSpanProcessor;\nimport io.opentelemetry.semconv.resource.attributes.ResourceAttributes;\nimport io.opentracing.Tracer;\nimport io.temporal.opentracing.OpenTracingOptions;\nimport io.temporal.opentracing.OpenTracingSpanContextCodec;\nimport java.util.concurrent.TimeUnit;\nimport org.apache.thrift.transport.TTransportException;\n\npublic class JaegerUtils {\n\n  public static OpenTracingOptions getJaegerOptions(String type) {\n    if (type.equals(\"OpenTracing\")) {\n      return getJaegerOpenTracingOptions();\n    }\n    // default to Open Telemetry\n    return getJaegerOpenTelemetryOptions();\n  }\n\n  private static OpenTracingOptions getJaegerOpenTracingOptions() {\n    try {\n      // Using Udp Sender for OpenTracing, make sure to change host and port\n      // to your Jaeger options (if using different than in sample)\n      RemoteReporter reporter =\n          new RemoteReporter.Builder().withSender(new UdpSender(\"localhost\", 5775, 0)).build();\n      Sampler sampler = new ConstSampler(true);\n      Tracer tracer =\n          new JaegerTracer.Builder(\"temporal-sample-opentracing\")\n              .withReporter(reporter)\n              .withSampler(sampler)\n              .build();\n\n      return getOpenTracingOptionsForTracer(tracer);\n    } catch (TTransportException e) {\n      System.out.println(\"Exception configuring Jaeger Tracer: \" + e.getMessage());\n      return null;\n    }\n  }\n\n  private static OpenTracingOptions getJaegerOpenTelemetryOptions() {\n    Resource serviceNameResource =\n        Resource.create(\n            Attributes.of(ResourceAttributes.SERVICE_NAME, \"temporal-sample-opentelemetry\"));\n\n    JaegerGrpcSpanExporter jaegerExporter =\n        JaegerGrpcSpanExporter.builder()\n            .setEndpoint(\"http://localhost:14250\")\n            .setTimeout(1, TimeUnit.SECONDS)\n            .build();\n\n    SdkTracerProvider tracerProvider =\n        SdkTracerProvider.builder()\n            .addSpanProcessor(SimpleSpanProcessor.create(jaegerExporter))\n            .setResource(Resource.getDefault().merge(serviceNameResource))\n            .build();\n\n    OpenTelemetrySdk openTelemetry =\n        OpenTelemetrySdk.builder()\n            .setPropagators(\n                ContextPropagators.create(\n                    TextMapPropagator.composite(\n                        W3CTraceContextPropagator.getInstance(), JaegerPropagator.getInstance())))\n            .setTracerProvider(tracerProvider)\n            .build();\n\n    // create OpenTracing shim and return OpenTracing Tracer from it\n    return getOpenTracingOptionsForTracer(OpenTracingShim.createTracerShim(openTelemetry));\n  }\n\n  private static OpenTracingOptions getOpenTracingOptionsForTracer(Tracer tracer) {\n    OpenTracingOptions options =\n        OpenTracingOptions.newBuilder()\n            .setSpanContextCodec(OpenTracingSpanContextCodec.TEXT_MAP_CODEC)\n            .setTracer(tracer)\n            .build();\n    return options;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/README.md",
    "content": "# Java SDK OpenTracing and OpenTelemetry Sample\n\nThis sample shows the [Temporal Java SDK OpenTracing](https://github.com/temporalio/sdk-java/tree/master/temporal-opentracing) support.\nIt shows how to set up OpenTracing, as well as OpenTelemetry.\nThe sample uses the [CNCF Jaeger](https://github.com/jaegertracing/jaeger) distributed tracing\nplatform.\n\n## Run the sample\n\nNote, it is assumed that you have Temporal Server set up and running using Docker Compose.\nFor more information on how to do this see the [main readme](../../../../../../../README.md).\n\n1. Start Jaeger via Docker:\n\n```bash\ndocker run -d -p 5775:5775/udp -p 14250:14250 -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest\n```\n\nThis starts Jaeger with udp port 5775 and grpc port 14250. Note that \nif these ports are different in your setup to reflect the changes in [JagerUtils](JaegerUtils.java).\n\n1. Start the Worker:\n\n* For OpenTelemetry:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.opentracing.TracingWorker\n```\n\n* For OpenTracing:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.opentracing.TracingWorker --args=\"OpenTracing\"\n```\n\n2. Start the Starter\n\n* For OpenTelemetry\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.opentracing.Starter\n```\n\n* For OpenTracing\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.opentracing.Starter --args=\"OpenTracing\"\n```\n\n3. Go to your Jaeger UI on [http://127.0.0.1:16686/search](http://127.0.0.1:16686/search)\n\n4. In the \"Service\" section select either \"temporal-sample-opentelemetry\" or \n   \"temporal-sample-opentracing\", depending on your starting options.\n   \n5. Check out the Operation dropdown to see all the different operations available\n\n6. In the Tags search input you can tag a specific workflow id, for example:\n\n```\nworkflowId=tracingWorkflow\n```\n\n7. Click on \"Find Traces\" in the Jager UI and see all look at all the spans info\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/Starter.java",
    "content": "package io.temporal.samples.tracing;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.opentracing.OpenTracingClientInterceptor;\nimport io.temporal.samples.tracing.workflow.TracingWorkflow;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\n\npublic class Starter {\n  public static final String TASK_QUEUE_NAME = \"tracingTaskQueue\";\n\n  public static void main(String[] args) {\n    String type = \"OpenTelemetry\";\n    if (args.length == 1) {\n      type = args[0];\n    }\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n\n    // Set the OpenTracing client interceptor, preserving env config\n    WorkflowClientOptions clientOptions =\n        profile.toWorkflowClientOptions().toBuilder()\n            .setInterceptors(new OpenTracingClientInterceptor(JaegerUtils.getJaegerOptions(type)))\n            .build();\n    WorkflowClient client = WorkflowClient.newInstance(service, clientOptions);\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setWorkflowId(\"tracingWorkflow\")\n            .setTaskQueue(TASK_QUEUE_NAME)\n            .build();\n\n    // Create typed workflow stub\n    TracingWorkflow workflow = client.newWorkflowStub(TracingWorkflow.class, workflowOptions);\n\n    // Convert to untyped and start it with signalWithStart\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    untyped.signalWithStart(\"setLanguage\", new Object[] {\"Spanish\"}, new Object[] {\"John\"});\n\n    String greeting = untyped.getResult(String.class);\n\n    System.out.println(\"Greeting: \" + greeting);\n\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/TracingWorker.java",
    "content": "package io.temporal.samples.tracing;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.opentracing.OpenTracingWorkerInterceptor;\nimport io.temporal.samples.tracing.workflow.TracingActivitiesImpl;\nimport io.temporal.samples.tracing.workflow.TracingChildWorkflowImpl;\nimport io.temporal.samples.tracing.workflow.TracingWorkflowImpl;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.io.IOException;\n\npublic class TracingWorker {\n  public static final String TASK_QUEUE_NAME = \"tracingTaskQueue\";\n\n  public static void main(String[] args) {\n    String type = \"OpenTelemetry\";\n    if (args.length == 1) {\n      type = args[0];\n    }\n\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Set the OpenTracing client interceptor\n    WorkerFactoryOptions factoryOptions =\n        WorkerFactoryOptions.newBuilder()\n            .setWorkerInterceptors(\n                new OpenTracingWorkerInterceptor(JaegerUtils.getJaegerOptions(type)))\n            .build();\n    WorkerFactory factory = WorkerFactory.newInstance(client, factoryOptions);\n\n    Worker worker = factory.newWorker(TASK_QUEUE_NAME);\n    worker.registerWorkflowImplementationTypes(\n        TracingWorkflowImpl.class, TracingChildWorkflowImpl.class);\n    worker.registerActivitiesImplementations(new TracingActivitiesImpl());\n\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingActivities.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface TracingActivities {\n  String greet(String name, String language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingActivitiesImpl.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\npublic class TracingActivitiesImpl implements TracingActivities {\n  @Override\n  public String greet(String name, String language) {\n    String greeting;\n\n    switch (language) {\n      case \"Spanish\":\n        greeting = \"Hola \" + name;\n        break;\n      case \"French\":\n        greeting = \"Bonjour \" + name;\n        break;\n      default:\n        greeting = \"Hello \" + name;\n    }\n\n    return greeting;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingChildWorkflow.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface TracingChildWorkflow {\n  @WorkflowMethod\n  String greet(String name, String language);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingChildWorkflowImpl.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\npublic class TracingChildWorkflowImpl implements TracingChildWorkflow {\n  @Override\n  public String greet(String name, String language) {\n\n    ActivityOptions activityOptions =\n        ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build();\n    TracingActivities activities =\n        Workflow.newActivityStub(TracingActivities.class, activityOptions);\n\n    return activities.greet(name, language);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingWorkflow.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface TracingWorkflow {\n\n  @WorkflowMethod\n  String greet(String name);\n\n  @SignalMethod\n  void setLanguage(String language);\n\n  @QueryMethod\n  String getLanguage();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/tracing/workflow/TracingWorkflowImpl.java",
    "content": "package io.temporal.samples.tracing.workflow;\n\nimport io.temporal.workflow.ChildWorkflowOptions;\nimport io.temporal.workflow.Workflow;\n\npublic class TracingWorkflowImpl implements TracingWorkflow {\n\n  private String language = \"English\";\n\n  @Override\n  public String greet(String name) {\n    ChildWorkflowOptions options =\n        ChildWorkflowOptions.newBuilder().setWorkflowId(\"tracingChildWorkflow\").build();\n\n    // Get the child workflow stub\n    TracingChildWorkflow child = Workflow.newChildWorkflowStub(TracingChildWorkflow.class, options);\n\n    // Invoke child sync and return its result\n    return child.greet(name, language);\n  }\n\n  @Override\n  public void setLanguage(String language) {\n    this.language = language;\n  }\n\n  @Override\n  public String getLanguage() {\n    return language;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflow.java",
    "content": "package io.temporal.samples.updatabletimer;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface DynamicSleepWorkflow {\n  @WorkflowMethod\n  void execute(long wakeUpTime);\n\n  @SignalMethod\n  void updateWakeUpTime(long wakeUpTime);\n\n  @QueryMethod\n  long getWakeUpTime();\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflowImpl.java",
    "content": "package io.temporal.samples.updatabletimer;\n\npublic class DynamicSleepWorkflowImpl implements DynamicSleepWorkflow {\n\n  private UpdatableTimer timer = new UpdatableTimer();\n\n  @Override\n  public void execute(long wakeUpTime) {\n    timer.sleepUntil(wakeUpTime);\n  }\n\n  @Override\n  public void updateWakeUpTime(long wakeUpTime) {\n    timer.updateWakeUpTime(wakeUpTime);\n  }\n\n  @Override\n  public long getWakeUpTime() {\n    return timer.getWakeUpTime();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflowStarter.java",
    "content": "package io.temporal.samples.updatabletimer;\n\nimport static io.temporal.samples.updatabletimer.DynamicSleepWorkflowWorker.DYNAMIC_SLEEP_WORKFLOW_ID;\nimport static io.temporal.samples.updatabletimer.DynamicSleepWorkflowWorker.TASK_QUEUE;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.WorkflowIdReusePolicy;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowExecutionAlreadyStarted;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DynamicSleepWorkflowStarter {\n\n  private static final Logger logger = LoggerFactory.getLogger(DynamicSleepWorkflowStarter.class);\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    DynamicSleepWorkflow workflow =\n        client.newWorkflowStub(\n            DynamicSleepWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(TASK_QUEUE)\n                .setWorkflowId(DYNAMIC_SLEEP_WORKFLOW_ID)\n                .setWorkflowIdReusePolicy(\n                    WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_ALLOW_DUPLICATE)\n                .build());\n\n    try {\n      // Start asynchronously\n      WorkflowExecution execution =\n          WorkflowClient.start(workflow::execute, System.currentTimeMillis() + 60000);\n      logger.info(\"Workflow started: \" + execution);\n    } catch (WorkflowExecutionAlreadyStarted e) {\n      logger.info(\"Workflow already running: \" + e.getExecution());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/DynamicSleepWorkflowWorker.java",
    "content": "package io.temporal.samples.updatabletimer;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactory;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class DynamicSleepWorkflowWorker {\n\n  static final String TASK_QUEUE = \"TimerUpdate\";\n\n  private static final Logger logger = LoggerFactory.getLogger(DynamicSleepWorkflowWorker.class);\n\n  /** Create just one workflow instance for the sake of the sample. */\n  static final String DYNAMIC_SLEEP_WORKFLOW_ID = \"DynamicSleepWorkflow\";\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    final Worker worker = factory.newWorker(TASK_QUEUE);\n    worker.registerWorkflowImplementationTypes(DynamicSleepWorkflowImpl.class);\n    factory.start();\n    logger.info(\"Worker started for task queue: \" + TASK_QUEUE);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/README.md",
    "content": "# Updatable Timer Sample\n\nDemonstrates a helper class which relies on `Workflow.await` to implement a blocking sleep that can be updated at any moment.\n\nThe sample is composed of the three executables:\n\n* `DynamicSleepWorkflowWorker` hosts the Workflow Executions.\n* `DynamicSleepWorkflowStarter` starts Workflow Executions.\n* `WakeUpTimeUpdater` Signals the Workflow Execution with the new time to wake up.\n\nFirst start the Worker:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.updatabletimer.DynamicSleepWorkflowWorker\n```\n\nThen in a different terminal window start the Workflow Execution:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.updatabletimer.DynamicSleepWorkflowStarter\n```\n\nCheck the output of the Worker window. The expected output is:\n\n```\n[...]\n11:39:08.852 [main] INFO  i.t.s.u.DynamicSleepWorkflowWorker - Worker started for task queue: TimerUpdate\n11:39:31.614 [workflow-method] INFO  i.t.s.updatabletimer.UpdatableTimer - sleepUntil: 2021-11-30T19:40:30.979Z\n11:39:31.615 [workflow-method] INFO  i.t.s.updatabletimer.UpdatableTimer - Going to sleep for PT59.727S\n```\n\nThen run the updater as many times as you want to change timer to 10 seconds from now:\n\n```bash\n./gradlew -q execute -PmainClass=io.temporal.samples.updatabletimer.WakeUpTimeUpdater\n./gradlew -q execute -PmainClass=io.temporal.samples.updatabletimer.WakeUpTimeUpdater\n```\n\nCheck the output of the worker window. The expected output is:\n\n```\n[...]\n11:39:37.740 [signal updateWakeUpTime] INFO  i.t.s.updatabletimer.UpdatableTimer - updateWakeUpTime: 2021-11-30T19:39:47.552Z\n11:39:37.740 [workflow-method] INFO  i.t.s.updatabletimer.UpdatableTimer - Going to sleep for PT9.841S\n11:39:44.679 [signal updateWakeUpTime] INFO  i.t.s.updatabletimer.UpdatableTimer - updateWakeUpTime: 2021-11-30T19:39:54.494Z\n11:39:44.680 [workflow-method] INFO  i.t.s.updatabletimer.UpdatableTimer - Going to sleep for PT9.838S\n11:39:54.565 [workflow-method] INFO  i.t.s.updatabletimer.UpdatableTimer - sleepUntil completed\n```"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/UpdatableTimer.java",
    "content": "package io.temporal.samples.updatabletimer;\n\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.time.Instant;\nimport org.slf4j.Logger;\n\npublic final class UpdatableTimer {\n\n  private final Logger logger = Workflow.getLogger(UpdatableTimer.class);\n\n  private long wakeUpTime;\n  private boolean wakeUpTimeUpdated;\n\n  public void sleepUntil(long wakeUpTime) {\n    logger.info(\"sleepUntil: \" + Instant.ofEpochMilli(wakeUpTime));\n    this.wakeUpTime = wakeUpTime;\n    while (true) {\n      wakeUpTimeUpdated = false;\n      Duration sleepInterval = Duration.ofMillis(this.wakeUpTime - Workflow.currentTimeMillis());\n      logger.info(\"Going to sleep for \" + sleepInterval);\n      if (!Workflow.await(sleepInterval, () -> wakeUpTimeUpdated)) {\n        break;\n      }\n    }\n    logger.info(\"sleepUntil completed\");\n  }\n\n  public void updateWakeUpTime(long wakeUpTime) {\n    logger.info(\"updateWakeUpTime: \" + Instant.ofEpochMilli(wakeUpTime));\n    this.wakeUpTime = wakeUpTime;\n    this.wakeUpTimeUpdated = true;\n  }\n\n  public long getWakeUpTime() {\n    return wakeUpTime;\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/updatabletimer/WakeUpTimeUpdater.java",
    "content": "package io.temporal.samples.updatabletimer;\n\nimport static io.temporal.samples.updatabletimer.DynamicSleepWorkflowWorker.DYNAMIC_SLEEP_WORKFLOW_ID;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class WakeUpTimeUpdater {\n\n  private static final Logger logger = LoggerFactory.getLogger(WakeUpTimeUpdater.class);\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Create a stub that points to an existing workflow with the given ID\n    DynamicSleepWorkflow workflow =\n        client.newWorkflowStub(DynamicSleepWorkflow.class, DYNAMIC_SLEEP_WORKFLOW_ID);\n\n    // signal workflow about the wake up time change\n    workflow.updateWakeUpTime(System.currentTimeMillis() + 10000);\n    logger.info(\"Updated wake up time to 10 seconds from now\");\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/Activities.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport com.fasterxml.jackson.annotation.JsonCreator;\nimport com.fasterxml.jackson.annotation.JsonProperty;\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\n\n@ActivityInterface\npublic interface Activities {\n\n  @ActivityMethod\n  String someActivity(String calledBy);\n\n  @ActivityMethod\n  String someIncompatibleActivity(IncompatibleActivityInput input);\n\n  class IncompatibleActivityInput {\n    private final String calledBy;\n    private final String moreData;\n\n    @JsonCreator(mode = JsonCreator.Mode.PROPERTIES)\n    public IncompatibleActivityInput(\n        @JsonProperty(\"calledBy\") String calledBy, @JsonProperty(\"moreData\") String moreData) {\n      this.calledBy = calledBy;\n      this.moreData = moreData;\n    }\n\n    @JsonProperty(\"calledBy\")\n    public String getCalledBy() {\n      return calledBy;\n    }\n\n    @JsonProperty(\"moreData\")\n    public String getMoreData() {\n      return moreData;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/ActivitiesImpl.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class ActivitiesImpl implements Activities {\n\n  private static final Logger logger = LoggerFactory.getLogger(ActivitiesImpl.class);\n\n  @Override\n  public String someActivity(String calledBy) {\n    logger.info(\"SomeActivity called by {}\", calledBy);\n    return \"SomeActivity called by \" + calledBy;\n  }\n\n  @Override\n  public String someIncompatibleActivity(IncompatibleActivityInput input) {\n    logger.info(\n        \"SomeIncompatibleActivity called by {} with {}\", input.getCalledBy(), input.getMoreData());\n    return \"SomeIncompatibleActivity called by \"\n        + input.getCalledBy()\n        + \" with \"\n        + input.getMoreData();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/AutoUpgradingWorkflow.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface AutoUpgradingWorkflow {\n\n  @WorkflowMethod\n  void run();\n\n  @SignalMethod\n  void doNextSignal(String signal);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/AutoUpgradingWorkflowV1Impl.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.VersioningBehavior;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowVersioningBehavior;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\n/**\n * This workflow will automatically move to the latest worker version. We'll be making changes to\n * it, which must be replay safe. Note that generally you won't want or need to include a version\n * number in your workflow name if you're using the worker versioning feature. This sample does it\n * to illustrate changes to the same code over time - but really what we're demonstrating here is\n * the evolution of what would have been one workflow definition.\n */\npublic class AutoUpgradingWorkflowV1Impl implements AutoUpgradingWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(AutoUpgradingWorkflowV1Impl.class);\n\n  private final List<String> signals = new ArrayList<>();\n  private final Activities activities =\n      Workflow.newActivityStub(\n          Activities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  @Override\n  @WorkflowVersioningBehavior(VersioningBehavior.AUTO_UPGRADE)\n  public void run() {\n    logger.info(\"Changing workflow v1 started. StartTime: {}\", Workflow.currentTimeMillis());\n\n    while (true) {\n      Workflow.await(() -> !signals.isEmpty());\n      String signal = signals.remove(0);\n\n      if (\"do-activity\".equals(signal)) {\n        logger.info(\"Changing workflow v1 running activity\");\n        activities.someActivity(\"v1\");\n      } else {\n        logger.info(\"Concluding workflow v1\");\n        return;\n      }\n    }\n  }\n\n  @Override\n  public void doNextSignal(String signal) {\n    signals.add(signal);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/AutoUpgradingWorkflowV1bImpl.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.VersioningBehavior;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowVersioningBehavior;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\n/**\n * This represents us having made *compatible* changes to AutoUpgradingWorkflowV1Impl.\n *\n * <p>The compatible changes we've made are:\n *\n * <ul>\n *   <li>Altering the log lines\n *   <li>Using the `Workflow.getVersion` API to properly introduce branching behavior while\n *       maintaining compatibility\n * </ul>\n */\npublic class AutoUpgradingWorkflowV1bImpl implements AutoUpgradingWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(AutoUpgradingWorkflowV1bImpl.class);\n\n  private final List<String> signals = new ArrayList<>();\n  private final Activities activities =\n      Workflow.newActivityStub(\n          Activities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  @Override\n  @WorkflowVersioningBehavior(VersioningBehavior.AUTO_UPGRADE)\n  public void run() {\n    logger.info(\"Changing workflow v1b started. StartTime: {}\", Workflow.currentTimeMillis());\n\n    while (true) {\n      Workflow.await(() -> !signals.isEmpty());\n      String signal = signals.remove(0);\n\n      if (\"do-activity\".equals(signal)) {\n        logger.info(\"Changing workflow v1b running activity\");\n        int version = Workflow.getVersion(\"DifferentActivity\", Workflow.DEFAULT_VERSION, 1);\n        if (version == 1) {\n          activities.someIncompatibleActivity(\n              new Activities.IncompatibleActivityInput(\"v1b\", \"hello!\"));\n        } else {\n          // Note it is a valid compatible change to alter the input to an activity.\n          // However, because we're using the getVersion API, this branch will never be\n          // taken.\n          activities.someActivity(\"v1b\");\n        }\n      } else {\n        logger.info(\"Concluding workflow v1b\");\n        break;\n      }\n    }\n  }\n\n  @Override\n  public void doNextSignal(String signal) {\n    signals.add(signal);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/PinnedWorkflow.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface PinnedWorkflow {\n\n  @WorkflowMethod\n  void run();\n\n  @SignalMethod\n  void doNextSignal(String signal);\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/PinnedWorkflowV1Impl.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.VersioningBehavior;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowVersioningBehavior;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\n/**\n * This workflow represents one that likely has a short lifetime, and we want to always stay pinned\n * to the same version it began on. Note that generally you won't want or need to include a version\n * number in your workflow name if you're using the worker versioning feature. This sample does it\n * to illustrate changes to the same code over time - but really what we're demonstrating here is\n * the evolution of what would have been one workflow definition.\n */\npublic class PinnedWorkflowV1Impl implements PinnedWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(PinnedWorkflowV1Impl.class);\n\n  private final List<String> signals = new ArrayList<>();\n  private final Activities activities =\n      Workflow.newActivityStub(\n          Activities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  @Override\n  @WorkflowVersioningBehavior(VersioningBehavior.PINNED)\n  public void run() {\n    logger.info(\"Pinned Workflow v1 started. StartTime: {}\", Workflow.currentTimeMillis());\n\n    while (true) {\n      Workflow.await(() -> !signals.isEmpty());\n      String signal = signals.remove(0);\n      if (\"conclude\".equals(signal)) {\n        break;\n      }\n    }\n\n    activities.someActivity(\"Pinned-v1\");\n  }\n\n  @Override\n  public void doNextSignal(String signal) {\n    signals.add(signal);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/PinnedWorkflowV2Impl.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.VersioningBehavior;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowVersioningBehavior;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.slf4j.Logger;\n\n/**\n * This workflow has changes that would make it incompatible with v1, and aren't protected by a\n * patch.\n */\npublic class PinnedWorkflowV2Impl implements PinnedWorkflow {\n\n  private static final Logger logger = Workflow.getLogger(PinnedWorkflowV2Impl.class);\n\n  private final List<String> signals = new ArrayList<>();\n  private final Activities activities =\n      Workflow.newActivityStub(\n          Activities.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(10)).build());\n\n  @Override\n  @WorkflowVersioningBehavior(VersioningBehavior.PINNED)\n  public void run() {\n    logger.info(\"Pinned Workflow v2 started. StartTime: {}\", Workflow.currentTimeMillis());\n\n    // Here we call an activity where we didn't before, which is an incompatible change.\n    activities.someActivity(\"Pinned-v2\");\n\n    while (true) {\n      Workflow.await(() -> !signals.isEmpty());\n      String signal = signals.remove(0);\n      if (\"conclude\".equals(signal)) {\n        break;\n      }\n    }\n\n    // We've also changed the activity type here, another incompatible change\n    activities.someIncompatibleActivity(\n        new Activities.IncompatibleActivityInput(\"Pinned-v2\", \"hi\"));\n  }\n\n  @Override\n  public void doNextSignal(String signal) {\n    signals.add(signal);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/README.md",
    "content": "# Worker Versioning\n\nThis sample demonstrates how to use Temporal's Worker Versioning feature to safely deploy updates to workflow and activity code. It shows the difference between auto-upgrading and pinned workflows, and how to manage worker deployments with different build IDs.\n\nThe sample creates multiple worker versions (1.0, 1.1, and 2.0) within one deployment and demonstrates:\n- **Auto-upgrading workflows**: Automatically and controllably migrate to newer worker versions\n- **Pinned workflows**: Stay on the original worker version throughout their lifecycle\n- **Compatible vs incompatible changes**: How to make safe updates using `Workflow.getVersion`\n\n## Steps to run this sample:\n\n1) Run a [Temporal service](https://github.com/temporalio/samples-java/tree/main/#how-to-use). And\n   ensure that you're using at least Server version 1.28.0 (CLI version 1.4.0).\n\n2) Start the main application (this will guide you through the sample):\n    ```bash\n    ./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.Starter\n    ```\n3) Follow the prompts to start workers in separate terminals:\n   - When prompted, run: `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV1`\n   - When prompted, run: `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV1_1`\n   - When prompted, run: `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV2`\n\nFollow the prompts in the example to observe auto-upgrading workflows migrating to newer workers\nwhile pinned workflows remain on their original versions.\n\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/Starter.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.api.workflowservice.v1.DescribeWorkerDeploymentRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkerDeploymentResponse;\nimport io.temporal.api.workflowservice.v1.SetWorkerDeploymentCurrentVersionRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.common.WorkerDeploymentVersion;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport java.io.IOException;\nimport java.util.UUID;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class Starter {\n  public static final String TASK_QUEUE = \"worker-versioning\";\n  public static final String DEPLOYMENT_NAME = \"my-deployment\";\n\n  private static final Logger logger = LoggerFactory.getLogger(Starter.class);\n\n  public static void main(String[] args) throws Exception {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    // Wait for v1 worker and set as current version\n    logger.info(\n        \"Waiting for v1 worker to appear. Run `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV1` in another terminal\");\n    waitForWorkerAndMakeCurrent(client, service, \"1.0\");\n\n    // Start auto-upgrading and pinned workflows. Importantly, note that when we start the\n    // workflows,\n    // we are using a workflow type name which does *not* include the version number. We defined\n    // them\n    // with versioned names so we could show changes to the code, but here when the client invokes\n    // them, we're demonstrating that the client remains version-agnostic.\n    String autoUpgradeWorkflowId = \"worker-versioning-versioning-autoupgrade_\" + UUID.randomUUID();\n    WorkflowStub autoUpgradeExecution =\n        client.newUntypedWorkflowStub(\n            \"AutoUpgradingWorkflow\",\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(autoUpgradeWorkflowId)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    String pinnedWorkflowId = \"worker-versioning-versioning-pinned_\" + UUID.randomUUID();\n    WorkflowStub pinnedExecution =\n        client.newUntypedWorkflowStub(\n            \"PinnedWorkflow\",\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(pinnedWorkflowId)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n\n    // Start workflows asynchronously\n    autoUpgradeExecution.start();\n    pinnedExecution.start();\n\n    logger.info(\n        \"Started auto-upgrading workflow: {}\", autoUpgradeExecution.getExecution().getWorkflowId());\n    logger.info(\"Started pinned workflow: {}\", pinnedExecution.getExecution().getWorkflowId());\n\n    // Signal both workflows a few times to drive them\n    advanceWorkflows(autoUpgradeExecution, pinnedExecution);\n\n    // Now wait for the v1.1 worker to appear and become current\n    logger.info(\n        \"Waiting for v1.1 worker to appear. Run `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV1_1` in another terminal\");\n    waitForWorkerAndMakeCurrent(client, service, \"1.1\");\n\n    // Once it has, we will continue to advance the workflows.\n    // The auto-upgrade workflow will now make progress on the new worker, while the pinned one will\n    // keep progressing on the old worker.\n    advanceWorkflows(autoUpgradeExecution, pinnedExecution);\n\n    // Finally we'll start the v2 worker, and again it'll become the new current version\n    logger.info(\n        \"Waiting for v2 worker to appear. Run `./gradlew -q execute -PmainClass=io.temporal.samples.workerversioning.WorkerV2` in another terminal\");\n    waitForWorkerAndMakeCurrent(client, service, \"2.0\");\n\n    // Once it has we'll start one more new workflow, another pinned one, to demonstrate that new\n    // pinned workflows start on the current version.\n    String pinnedWorkflow2Id = \"worker-versioning-versioning-pinned-2_\" + UUID.randomUUID();\n    WorkflowStub pinnedExecution2 =\n        client.newUntypedWorkflowStub(\n            \"PinnedWorkflow\",\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(pinnedWorkflow2Id)\n                .setTaskQueue(TASK_QUEUE)\n                .build());\n    pinnedExecution2.start();\n    logger.info(\"Started pinned workflow v2: {}\", pinnedExecution2.getExecution().getWorkflowId());\n\n    // Now we'll conclude all workflows. You should be able to see in your server UI that the pinned\n    // workflow always stayed on 1.0, while the auto-upgrading workflow migrated.\n    autoUpgradeExecution.signal(\"doNextSignal\", \"conclude\");\n    pinnedExecution.signal(\"doNextSignal\", \"conclude\");\n    pinnedExecution2.signal(\"doNextSignal\", \"conclude\");\n\n    // Wait for all workflows to complete\n    autoUpgradeExecution.getResult(Void.class);\n    pinnedExecution.getResult(Void.class);\n    pinnedExecution2.getResult(Void.class);\n\n    logger.info(\"All workflows completed\");\n  }\n\n  private static void advanceWorkflows(\n      WorkflowStub autoUpgradeExecution, WorkflowStub pinnedExecution) {\n    // Signal both workflows a few times to drive them\n    for (int i = 0; i < 3; i++) {\n      autoUpgradeExecution.signal(\"doNextSignal\", \"do-activity\");\n      pinnedExecution.signal(\"doNextSignal\", \"some-signal\");\n    }\n  }\n\n  private static void waitForWorkerAndMakeCurrent(\n      WorkflowClient client, WorkflowServiceStubs service, String buildId)\n      throws InterruptedException {\n    WorkerDeploymentVersion targetVersion = new WorkerDeploymentVersion(DEPLOYMENT_NAME, buildId);\n\n    // Wait for the worker to appear\n    while (true) {\n      try {\n        DescribeWorkerDeploymentRequest describeRequest =\n            DescribeWorkerDeploymentRequest.newBuilder()\n                .setNamespace(client.getOptions().getNamespace())\n                .setDeploymentName(DEPLOYMENT_NAME)\n                .build();\n\n        DescribeWorkerDeploymentResponse response =\n            service.blockingStub().describeWorkerDeployment(describeRequest);\n\n        // Check if our version is present in the version summaries\n        boolean found =\n            response.getWorkerDeploymentInfo().getVersionSummariesList().stream()\n                .anyMatch(\n                    versionSummary ->\n                        targetVersion\n                                .getDeploymentName()\n                                .equals(versionSummary.getDeploymentVersion().getDeploymentName())\n                            && targetVersion\n                                .getBuildId()\n                                .equals(versionSummary.getDeploymentVersion().getBuildId()));\n\n        if (found) {\n          break;\n        }\n      } catch (Exception ignored) {\n        System.out.println();\n      }\n      Thread.sleep(1000);\n    }\n\n    // Once the version is available, set it as current\n    SetWorkerDeploymentCurrentVersionRequest setRequest =\n        SetWorkerDeploymentCurrentVersionRequest.newBuilder()\n            .setNamespace(client.getOptions().getNamespace())\n            .setDeploymentName(DEPLOYMENT_NAME)\n            .setBuildId(targetVersion.getBuildId())\n            .build();\n\n    service.blockingStub().setWorkerDeploymentCurrentVersion(setRequest);\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/WorkerV1.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.common.WorkerDeploymentVersion;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerDeploymentOptions;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerOptions;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class WorkerV1 {\n\n  private static final Logger logger = LoggerFactory.getLogger(WorkerV1.class);\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerDeploymentVersion version = new WorkerDeploymentVersion(Starter.DEPLOYMENT_NAME, \"1.0\");\n    WorkerDeploymentOptions deploymentOptions =\n        WorkerDeploymentOptions.newBuilder().setUseVersioning(true).setVersion(version).build();\n\n    WorkerOptions workerOptions =\n        WorkerOptions.newBuilder().setDeploymentOptions(deploymentOptions).build();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(Starter.TASK_QUEUE, workerOptions);\n\n    worker.registerWorkflowImplementationTypes(\n        AutoUpgradingWorkflowV1Impl.class, PinnedWorkflowV1Impl.class);\n    worker.registerActivitiesImplementations(new ActivitiesImpl());\n\n    logger.info(\"Starting worker v1 (build 1.0)\");\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/WorkerV1_1.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.common.WorkerDeploymentVersion;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerDeploymentOptions;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerOptions;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class WorkerV1_1 {\n\n  private static final Logger logger = LoggerFactory.getLogger(WorkerV1_1.class);\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerDeploymentVersion version = new WorkerDeploymentVersion(Starter.DEPLOYMENT_NAME, \"1.1\");\n    WorkerDeploymentOptions deploymentOptions =\n        WorkerDeploymentOptions.newBuilder().setUseVersioning(true).setVersion(version).build();\n\n    WorkerOptions workerOptions =\n        WorkerOptions.newBuilder().setDeploymentOptions(deploymentOptions).build();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(Starter.TASK_QUEUE, workerOptions);\n\n    worker.registerWorkflowImplementationTypes(\n        AutoUpgradingWorkflowV1bImpl.class, PinnedWorkflowV1Impl.class);\n    worker.registerActivitiesImplementations(new ActivitiesImpl());\n\n    logger.info(\"Starting worker v1.1 (build 1.1)\");\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/java/io/temporal/samples/workerversioning/WorkerV2.java",
    "content": "package io.temporal.samples.workerversioning;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.common.WorkerDeploymentVersion;\nimport io.temporal.envconfig.ClientConfigProfile;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerDeploymentOptions;\nimport io.temporal.worker.WorkerFactory;\nimport io.temporal.worker.WorkerOptions;\nimport java.io.IOException;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\n\npublic class WorkerV2 {\n\n  private static final Logger logger = LoggerFactory.getLogger(WorkerV2.class);\n\n  public static void main(String[] args) {\n    // Load configuration from environment and files\n    ClientConfigProfile profile;\n    try {\n      profile = ClientConfigProfile.load();\n    } catch (IOException e) {\n      throw new RuntimeException(\"Failed to load client configuration\", e);\n    }\n\n    WorkflowServiceStubs service =\n        WorkflowServiceStubs.newServiceStubs(profile.toWorkflowServiceStubsOptions());\n    WorkflowClient client = WorkflowClient.newInstance(service, profile.toWorkflowClientOptions());\n\n    WorkerDeploymentVersion version = new WorkerDeploymentVersion(Starter.DEPLOYMENT_NAME, \"2.0\");\n    WorkerDeploymentOptions deploymentOptions =\n        WorkerDeploymentOptions.newBuilder().setUseVersioning(true).setVersion(version).build();\n\n    WorkerOptions workerOptions =\n        WorkerOptions.newBuilder().setDeploymentOptions(deploymentOptions).build();\n\n    WorkerFactory factory = WorkerFactory.newInstance(client);\n    Worker worker = factory.newWorker(Starter.TASK_QUEUE, workerOptions);\n\n    worker.registerWorkflowImplementationTypes(\n        AutoUpgradingWorkflowV1bImpl.class, PinnedWorkflowV2Impl.class);\n    worker.registerActivitiesImplementations(new ActivitiesImpl());\n\n    logger.info(\"Starting worker v2 (build 2.0)\");\n    factory.start();\n  }\n}\n"
  },
  {
    "path": "core/src/main/resources/config.toml",
    "content": "# This is a sample configuration file for demonstrating Temporal's environment\n# configuration feature. It defines multiple profiles for different environments,\n# such as local development, production, and staging.\n\n# Default profile for local development\n[profile.default]\naddress = \"localhost:7233\"\nnamespace = \"default\"\n\n# Optional: Add custom gRPC headers\n[profile.default.grpc_meta]\nmy-custom-header = \"development-value\"\ntrace-id = \"dev-trace-123\"\n\n# Staging profile with inline certificate data\n[profile.staging]\naddress = \"localhost:9999\"\nnamespace = \"staging\" \n\n# Nexus messaging sample profiles\n[profile.nexus-messaging-handler]\naddress = \"localhost:7233\"\nnamespace = \"nexus-messaging-handler-namespace\"\n\n[profile.nexus-messaging-caller]\naddress = \"localhost:7233\"\nnamespace = \"nexus-messaging-caller-namespace\"\n\n# An example production profile for Temporal Cloud\n[profile.prod]\naddress = \"your-namespace.a1b2c.tmprl.cloud:7233\"\nnamespace = \"your-namespace\"\n# Replace with your actual Temporal Cloud API key\napi_key = \"your-api-key-here\"\n\n# TLS configuration for production\n[profile.prod.tls]\n# TLS is auto-enabled when an API key is present, but you can configure it\n# explicitly.\n# disabled = false\n\n# Use certificate files for mTLS. Replace with actual paths.\nclient_cert_path = \"/etc/temporal/certs/client.pem\"\nclient_key_path = \"/etc/temporal/certs/client.key\"\n\n# Custom headers for production\n[profile.prod.grpc_meta]\nenvironment = \"production\"\nservice-version = \"v1.2.3\""
  },
  {
    "path": "core/src/main/resources/dsl/sampleflow.json",
    "content": "{\n  \"id\": \"sampleFlow\",\n  \"name\": \"Sample Flow One\",\n  \"description\": \"Sample Flow Definition\",\n  \"actions\": [\n    {\n      \"action\": \"One\",\n      \"retries\": 10,\n      \"startToCloseSec\": 3\n    },\n    {\n      \"action\": \"Two\",\n      \"retries\": 8,\n      \"startToCloseSec\": 3\n    },\n    {\n      \"action\": \"Three\",\n      \"retries\": 10,\n      \"startToCloseSec\": 4\n    },\n    {\n      \"action\": \"Four\",\n      \"retries\": 9,\n      \"startToCloseSec\": 5\n    }\n  ]\n}"
  },
  {
    "path": "core/src/main/resources/logback.xml",
    "content": "<!--\n     Copyright (c) 2020 Temporal Technologies, Inc. All Rights Reserved\n\n     Copyright 2012-2016 Amazon.com, Inc. or its affiliates. All Rights Reserved.\n\n     Modifications copyright (C) 2017 Uber Technologies, Inc.\n\n     Licensed under the Apache License, Version 2.0 (the \"License\"). You may not\n     use this file except in compliance with the License. A copy of the License is\n     located at\n\n     http://aws.amazon.com/apache2.0\n\n     or in the \"license\" file accompanying this file. This file is distributed on\n     an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either\n     express or implied. See the License for the specific language governing\n     permissions and limitations under the License.\n-->\n\n<configuration>\n    <appender name=\"STDOUT\" class=\"ch.qos.logback.core.ConsoleAppender\">\n        <!-- encoders are assigned the type\n             ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->\n        <encoder>\n            <!-- WorkflowId and ActivityId are keys populated by JavaSDK into MDC context -->\n            <!-- See io.temporal.internal.logging.LoggerTag for a full list of keys -->\n            <pattern>%d{HH:mm:ss.SSS} {%X{WorkflowId} %X{ActivityId}} [%thread] %-5level %logger{36} - %msg %n</pattern>\n        </encoder>\n    </appender>\n    <logger name=\"io.grpc.netty\" level=\"INFO\"/>\n    <root level=\"INFO\">\n        <appender-ref ref=\"STDOUT\" />\n    </root>\n</configuration>"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/asyncchild/AsyncChildTest.java",
    "content": "package io.temporal.samples.asyncchild;\n\nimport static org.junit.Assert.assertNotNull;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class AsyncChildTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(ParentWorkflowImpl.class, ChildWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testAsyncChildWorkflow() {\n    ParentWorkflow parentWorkflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                ParentWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowExecution childExecution = parentWorkflow.executeParent();\n\n    assertNotNull(childExecution);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/asyncuntypedchild/AsyncUntypedChildTest.java",
    "content": "package io.temporal.samples.asyncuntypedchild;\n\nimport static io.temporal.api.enums.v1.WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_RUNNING;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.WorkflowExecutionStatus;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link ParentWorkflowImpl}. Doesn't use an external Temporal service. */\npublic class AsyncUntypedChildTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setDoNotStart(true).build();\n\n  @Test\n  public void testMockedChild() {\n    testWorkflowRule.getWorker().registerWorkflowImplementationTypes(ParentWorkflowImpl.class);\n\n    // Factory is called to create a new workflow object on each workflow task.\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationFactory(\n            ChildWorkflow.class,\n            () -> {\n              ChildWorkflow child = mock(ChildWorkflow.class);\n              when(child.composeGreeting(\"Hello\", \"World\"))\n                  .thenReturn(\"Hello World from mocked child!\");\n              return child;\n            });\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowClient workflowClient = testWorkflowRule.getWorkflowClient();\n    ParentWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            ParentWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    // Execute the parent workflow and wait for it to complete.\n    String childWorkflowId = workflow.getGreeting(\"World\");\n    assertNotNull(childWorkflowId);\n\n    assertEquals(\n        WORKFLOW_EXECUTION_STATUS_RUNNING,\n        getChildWorkflowExecutionStatus(workflowClient, childWorkflowId));\n\n    // Wait for the child to complete\n    String childResult =\n        workflowClient.newUntypedWorkflowStub(childWorkflowId).getResult(String.class);\n    assertEquals(\"Hello World from mocked child!\", childResult);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @NotNull\n  private WorkflowExecutionStatus getChildWorkflowExecutionStatus(\n      WorkflowClient workflowClient, String childWorkflowId) {\n    return workflowClient\n        .getWorkflowServiceStubs()\n        .blockingStub()\n        .describeWorkflowExecution(\n            DescribeWorkflowExecutionRequest.newBuilder()\n                .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n                .setExecution(WorkflowExecution.newBuilder().setWorkflowId(childWorkflowId).build())\n                .build())\n        .getWorkflowExecutionInfo()\n        .getStatus();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/batch/heartbeatingactivity/HeartbeatingActivityBatchWorkflowTest.java",
    "content": "package io.temporal.samples.batch.heartbeatingactivity;\n\nimport static io.temporal.samples.batch.heartbeatingactivity.RecordLoaderImpl.RECORD_COUNT;\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HeartbeatingActivityBatchWorkflowTest {\n  private static boolean[] processedRecords = new boolean[RECORD_COUNT];\n\n  public static class TestRecordProcessorImpl implements RecordProcessor {\n\n    @Override\n    public void processRecord(SingleRecord r) {\n      processedRecords[r.getId()] = true;\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HeartbeatingActivityBatchWorkflowImpl.class)\n          .setActivityImplementations(\n              new RecordProcessorActivityImpl(\n                  new RecordLoaderImpl(), new TestRecordProcessorImpl()))\n          .build();\n\n  @Test\n  public void testBatchWorkflow() {\n    HeartbeatingActivityBatchWorkflow workflow =\n        testWorkflowRule.newWorkflowStub(HeartbeatingActivityBatchWorkflow.class);\n    workflow.processBatch();\n\n    for (int i = 0; i < processedRecords.length; i++) {\n      assertTrue(processedRecords[i]);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/batch/iterator/IteratorIteratorBatchWorkflowTest.java",
    "content": "package io.temporal.samples.batch.iterator;\n\nimport static io.temporal.samples.batch.iterator.RecordLoaderImpl.PAGE_COUNT;\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.workflow.Workflow;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class IteratorIteratorBatchWorkflowTest {\n\n  private static final int PAGE_SIZE = 10;\n\n  /** The sample RecordLoaderImpl always returns the fixed number pages. */\n  private static boolean[] processedRecords = new boolean[PAGE_SIZE * PAGE_COUNT];\n\n  public static class TestRecordProcessorWorkflowImpl implements RecordProcessorWorkflow {\n\n    @Override\n    public void processRecord(SingleRecord r) {\n      Workflow.sleep(5000);\n      processedRecords[r.getId()] = true;\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(IteratorBatchWorkflowImpl.class, TestRecordProcessorWorkflowImpl.class)\n          .setActivityImplementations(new RecordLoaderImpl())\n          .build();\n\n  @Test\n  public void testBatchWorkflow() {\n    IteratorBatchWorkflow workflow = testWorkflowRule.newWorkflowStub(IteratorBatchWorkflow.class);\n    workflow.processBatch(PAGE_SIZE, 0);\n\n    for (int i = 0; i < processedRecords.length; i++) {\n      assertTrue(processedRecords[i]);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/batch/slidingwindow/SlidingWindowBatchWorkflowTest.java",
    "content": "package io.temporal.samples.batch.slidingwindow;\n\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInfo;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class SlidingWindowBatchWorkflowTest {\n\n  private static final int RECORD_COUNT = 15;\n  private static boolean[] processedRecords = new boolean[RECORD_COUNT];\n\n  public static class TestRecordProcessorWorkflowImpl implements RecordProcessorWorkflow {\n\n    @Override\n    public void processRecord(SingleRecord r) {\n      processedRecords[r.getId()] = true;\n      WorkflowInfo info = Workflow.getInfo();\n      String parentId = info.getParentWorkflowId().get();\n      SlidingWindowBatchWorkflow parent =\n          Workflow.newExternalWorkflowStub(SlidingWindowBatchWorkflow.class, parentId);\n      Workflow.sleep(500);\n      // Notify parent about record processing completion\n      // Needs to retry due to a continue-as-new atomicity\n      // bug in the testservice:\n      // https://github.com/temporalio/sdk-java/issues/1538\n      while (true) {\n        try {\n          parent.reportCompletion(r.getId());\n          break;\n        } catch (Exception e) {\n          continue;\n        }\n      }\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(\n              SlidingWindowBatchWorkflowImpl.class, TestRecordProcessorWorkflowImpl.class)\n          .setActivityImplementations(new RecordLoaderImpl())\n          .build();\n\n  @Test\n  public void testSlidingWindowBatchWorkflow() {\n    SlidingWindowBatchWorkflow workflow =\n        testWorkflowRule.newWorkflowStub(SlidingWindowBatchWorkflow.class);\n\n    ProcessBatchInput input = new ProcessBatchInput();\n    input.setPageSize(3);\n    input.setSlidingWindowSize(7);\n    input.setOffset(0);\n    input.setMaximumOffset(RECORD_COUNT);\n    workflow.processBatch(input);\n    for (int i = 0; i < RECORD_COUNT; i++) {\n      assertTrue(processedRecords[i]);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/bookingsaga/TripBookingWorkflowTest.java",
    "content": "package io.temporal.samples.bookingsaga;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\n\nimport io.temporal.client.WorkflowException;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.mockito.ArgumentCaptor;\n\npublic class TripBookingWorkflowTest {\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .setWorkflowTypes(TripBookingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  /**\n   * Not very useful test that validates that the default activities cause workflow to fail. See\n   * other tests on using mocked activities to test SAGA logic.\n   */\n  @Test\n  public void testTripBookingFails(\n      TestWorkflowEnvironment testEnv, Worker worker, TripBookingWorkflow workflow) {\n    worker.registerActivitiesImplementations(new TripBookingActivitiesImpl());\n    testEnv.start();\n\n    WorkflowException exception =\n        assertThrows(WorkflowException.class, () -> workflow.bookTrip(\"trip1\"));\n    assertEquals(\n        \"Flight booking did not work\",\n        ((ApplicationFailure) exception.getCause().getCause()).getOriginalMessage());\n  }\n\n  /** Unit test workflow logic using mocked activities. */\n  @Test\n  public void testSAGA(\n      TestWorkflowEnvironment testEnv, Worker worker, TripBookingWorkflow workflow) {\n    TripBookingActivities activities = mock(TripBookingActivities.class);\n\n    ArgumentCaptor<String> captorHotelRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.bookHotel(captorHotelRequestId.capture(), eq(\"trip1\")))\n        .thenReturn(\"HotelBookingID1\");\n\n    ArgumentCaptor<String> captorCarRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.reserveCar(captorCarRequestId.capture(), eq(\"trip1\")))\n        .thenReturn(\"CarBookingID1\");\n\n    ArgumentCaptor<String> captorFlightRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.bookFlight(captorFlightRequestId.capture(), eq(\"trip1\")))\n        .thenThrow(\n            ApplicationFailure.newNonRetryableFailure(\n                \"Flight booking did not work\", \"bookingFailure\"));\n    worker.registerActivitiesImplementations(activities);\n    testEnv.start();\n\n    WorkflowException exception =\n        assertThrows(WorkflowException.class, () -> workflow.bookTrip(\"trip1\"));\n    assertEquals(\n        \"Flight booking did not work\",\n        ((ApplicationFailure) exception.getCause().getCause()).getOriginalMessage());\n\n    verify(activities).cancelHotel(captorHotelRequestId.getValue(), \"trip1\");\n    verify(activities).cancelCar(captorCarRequestId.getValue(), \"trip1\");\n    verify(activities).cancelFlight(captorFlightRequestId.getValue(), \"trip1\");\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/bookingsyncsaga/TripBookingWorkflowTest.java",
    "content": "package io.temporal.samples.bookingsyncsaga;\n\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowException;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.client.WorkflowUpdateException;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Assertions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\nimport org.mockito.ArgumentCaptor;\n\npublic class TripBookingWorkflowTest {\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .setWorkflowTypes(TripBookingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  /**\n   * Not very useful test that validates that the default activities cause workflow to fail. See\n   * other tests on using mocked activities to test SAGA logic.\n   */\n  @Test\n  public void testTripBookingFails(\n      TestWorkflowEnvironment testEnv, Worker worker, TripBookingWorkflow workflow) {\n    worker.registerActivitiesImplementations(new TripBookingActivitiesImpl());\n    testEnv.start();\n\n    WorkflowException exception =\n        assertThrows(WorkflowException.class, () -> workflow.bookTrip(\"trip1\"));\n    Assertions.assertEquals(\n        \"Flight booking did not work\",\n        ((ApplicationFailure) exception.getCause().getCause()).getOriginalMessage());\n  }\n\n  /** Unit test workflow logic using mocked activities. */\n  @Test\n  public void testSAGA(\n      TestWorkflowEnvironment testEnv, Worker worker, TripBookingWorkflow workflow) {\n    TripBookingActivities activities = mock(TripBookingActivities.class);\n\n    ArgumentCaptor<String> captorHotelRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.bookHotel(captorHotelRequestId.capture(), eq(\"trip1\")))\n        .thenReturn(\"HotelBookingID1\");\n\n    ArgumentCaptor<String> captorCarRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.reserveCar(captorCarRequestId.capture(), eq(\"trip1\")))\n        .thenReturn(\"CarBookingID1\");\n\n    ArgumentCaptor<String> captorFlightRequestId = ArgumentCaptor.forClass(String.class);\n    when(activities.bookFlight(captorFlightRequestId.capture(), eq(\"trip1\")))\n        .thenThrow(\n            ApplicationFailure.newNonRetryableFailure(\n                \"Flight booking did not work\", \"bookingFailure\"));\n    worker.registerActivitiesImplementations(activities);\n    testEnv.start();\n\n    // Starts workflow asynchronously.\n    WorkflowClient.start(workflow::bookTrip, \"trip1\");\n\n    // Waits for update to return.\n    WorkflowException exception1 =\n        assertThrows(WorkflowUpdateException.class, () -> workflow.waitForBooking());\n    Assertions.assertEquals(\n        \"Flight booking did not work\",\n        ((ApplicationFailure) exception1.getCause().getCause()).getOriginalMessage());\n\n    // Waits for workflow to complete.\n    WorkflowStub stub = WorkflowStub.fromTyped(workflow);\n    WorkflowException exception2 =\n        assertThrows(WorkflowException.class, () -> stub.getResult(Void.class));\n    Assertions.assertEquals(\n        \"Flight booking did not work\",\n        ((ApplicationFailure) exception2.getCause().getCause()).getOriginalMessage());\n\n    verify(activities).cancelHotel(captorHotelRequestId.getValue(), \"trip1\");\n    verify(activities).cancelCar(captorCarRequestId.getValue(), \"trip1\");\n    verify(activities).cancelFlight(captorFlightRequestId.getValue(), \"trip1\");\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/countinterceptor/ClientCountInterceptorTest.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.samples.countinterceptor.activities.MyActivitiesImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyChildWorkflowImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflow;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class ClientCountInterceptorTest {\n\n  private static final String WORKFLOW_ID = \"TestInterceptorWorkflow\";\n\n  private final ClientCounter clientCounter = new ClientCounter();\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(MyWorkflowImpl.class, MyChildWorkflowImpl.class)\n          .setActivityImplementations(new MyActivitiesImpl())\n          .setWorkflowClientOptions(\n              WorkflowClientOptions.newBuilder()\n                  .setInterceptors(new SimpleClientInterceptor(clientCounter))\n                  .build())\n          .build();\n\n  @Test\n  public void testInterceptor() {\n    WorkflowClient workflowClient = testWorkflowRule.getWorkflowClient();\n\n    MyWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            MyWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(WORKFLOW_ID)\n                .build());\n\n    WorkflowClient.start(workflow::exec);\n\n    workflow.signalNameAndTitle(\"John\", \"Customer\");\n\n    String name = workflow.queryName();\n    String title = workflow.queryTitle();\n\n    workflow.exit();\n\n    // Wait for workflow completion via WorkflowStub\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    String result = untyped.getResult(String.class);\n\n    assertNotNull(result);\n\n    assertNotNull(name);\n    assertEquals(\"John\", name);\n    assertNotNull(title);\n    assertEquals(\"Customer\", title);\n\n    assertEquals(1, clientCounter.getNumOfWorkflowExecutions(WORKFLOW_ID));\n    assertEquals(1, clientCounter.getNumOfGetResults(WORKFLOW_ID));\n    assertEquals(2, clientCounter.getNumOfSignals(WORKFLOW_ID));\n    assertEquals(2, clientCounter.getNumOfQueries(WORKFLOW_ID));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/countinterceptor/WorkerCountInterceptorTest.java",
    "content": "package io.temporal.samples.countinterceptor;\n\nimport static org.junit.Assert.*;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.samples.countinterceptor.activities.MyActivitiesImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyChildWorkflowImpl;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflow;\nimport io.temporal.samples.countinterceptor.workflow.MyWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class WorkerCountInterceptorTest {\n\n  private static final String WORKFLOW_ID = \"TestInterceptorWorkflow\";\n  private static final String CHILD_WORKFLOW_ID = \"TestInterceptorChildWorkflow\";\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(MyWorkflowImpl.class, MyChildWorkflowImpl.class)\n          .setActivityImplementations(new MyActivitiesImpl())\n          .setWorkerFactoryOptions(\n              WorkerFactoryOptions.newBuilder()\n                  .setWorkerInterceptors(new SimpleCountWorkerInterceptor())\n                  .build())\n          .build();\n\n  @Test\n  public void testInterceptor() {\n    MyWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                MyWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowId(WORKFLOW_ID)\n                    .build());\n\n    WorkflowClient.start(workflow::exec);\n\n    workflow.signalNameAndTitle(\"John\", \"Customer\");\n\n    String name = workflow.queryName();\n    String title = workflow.queryTitle();\n\n    workflow.exit();\n\n    // Wait for workflow completion via WorkflowStub\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    String result = untyped.getResult(String.class);\n\n    assertNotNull(result);\n\n    assertNotNull(name);\n    assertEquals(\"John\", name);\n    assertNotNull(title);\n    assertEquals(\"Customer\", title);\n\n    assertEquals(1, WorkerCounter.getNumOfWorkflowExecutions(WORKFLOW_ID));\n    assertEquals(1, WorkerCounter.getNumOfChildWorkflowExecutions(WORKFLOW_ID));\n    // parent workflow does not execute any activities\n    assertEquals(0, WorkerCounter.getNumOfActivityExecutions(WORKFLOW_ID));\n    // child workflow executes 2 activities\n    assertEquals(2, WorkerCounter.getNumOfActivityExecutions(CHILD_WORKFLOW_ID));\n    assertEquals(2, WorkerCounter.getNumOfSignals(WORKFLOW_ID));\n    assertEquals(2, WorkerCounter.getNumOfQueries(WORKFLOW_ID));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/dsl/DslWorkflowTest.java",
    "content": "package io.temporal.samples.dsl;\n\nimport static org.junit.Assert.*;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.dsl.model.Flow;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class DslWorkflowTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(DslWorkflowImpl.class)\n          .setActivityImplementations(new DslActivitiesImpl())\n          .build();\n\n  @Test\n  public void testDslWorkflow() throws Exception {\n    DslWorkflow workflow =\n        testWorkflowRule\n            .getTestEnvironment()\n            .getWorkflowClient()\n            .newWorkflowStub(\n                DslWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"dsl-workflow\")\n                    .setTaskQueue(testWorkflowRule.getWorker().getTaskQueue())\n                    .build());\n\n    String result = workflow.run(getFlowFromResource(), \"test input\");\n    assertNotNull(result);\n    assertEquals(\n        \"Activity one done...,Activity two done...,Activity three done...,Activity four done...\",\n        result);\n  }\n\n  private static Flow getFlowFromResource() {\n    ObjectMapper objectMapper = new ObjectMapper();\n    try {\n      return objectMapper.readValue(\n          DslWorkflowTest.class.getClassLoader().getResource(\"dsl/sampleflow.json\"), Flow.class);\n    } catch (Exception e) {\n      e.printStackTrace();\n      return null;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/earlyreturn/TransactionWorkflowTest.java",
    "content": "package io.temporal.samples.earlyreturn;\n\nimport static org.junit.Assert.*;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.api.enums.v1.WorkflowIdConflictPolicy;\nimport io.temporal.client.*;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.UUID;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class TransactionWorkflowTest {\n\n  private static final String SOURCE_ACCOUNT = \"Bob\";\n  private static final String TARGET_ACCOUNT = \"Alice\";\n  private static final String TEST_TRANSACTION_ID = \"test-id-123\";\n  private static final int VALID_AMOUNT = 1000;\n  private static final int INVALID_AMOUNT = -1000;\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(TransactionWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testUpdateWithStartValidAmount() throws Exception {\n    // Mock activities\n    TransactionActivities activities =\n        mock(TransactionActivities.class, withSettings().withoutAnnotations());\n    when(activities.initTransaction(any()))\n        .thenReturn(\n            new Transaction(TEST_TRANSACTION_ID, SOURCE_ACCOUNT, TARGET_ACCOUNT, VALID_AMOUNT));\n\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Create workflow stub\n    WorkflowClient workflowClient = testWorkflowRule.getWorkflowClient();\n    TransactionWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            TransactionWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowIdConflictPolicy(\n                    WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_FAIL)\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .build());\n\n    // Execute UpdateWithStart\n    TransactionRequest txRequest =\n        new TransactionRequest(SOURCE_ACCOUNT, TARGET_ACCOUNT, VALID_AMOUNT);\n    TxResult updateResult =\n        WorkflowClient.executeUpdateWithStart(\n            workflow::returnInitResult,\n            UpdateOptions.<TxResult>newBuilder().build(),\n            new WithStartWorkflowOperation<>(workflow::processTransaction, txRequest));\n\n    // Verify both update and final results\n    assertEquals(TEST_TRANSACTION_ID, updateResult.getTransactionId());\n\n    TxResult finalResult = WorkflowStub.fromTyped(workflow).getResult(TxResult.class);\n    assertEquals(\"Transaction completed successfully.\", finalResult.getStatus());\n\n    // Verify activities were calledgit\n    verify(activities).mintTransactionId(any());\n    verify(activities).initTransaction(any());\n    verify(activities).completeTransaction(any());\n    verifyNoMoreInteractions(activities);\n  }\n\n  @Test\n  public void testUpdateWithStartInvalidAmount() throws Exception {\n    // Mock activities\n    TransactionActivities activities =\n        mock(TransactionActivities.class, withSettings().withoutAnnotations());\n    when(activities.initTransaction(any()))\n        .thenThrow(\n            ApplicationFailure.newNonRetryableFailure(\n                \"Non-retryable Activity Failure: Invalid Amount\", \"InvalidAmount\"));\n    doNothing().when(activities).cancelTransaction(any());\n\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Create workflow stub with explicit ID\n    WorkflowClient workflowClient = testWorkflowRule.getWorkflowClient();\n    String workflowId = \"test-workflow-\" + UUID.randomUUID();\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder()\n            .setWorkflowIdConflictPolicy(WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_FAIL)\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(workflowId)\n            .build();\n\n    TransactionWorkflow workflow =\n        workflowClient.newWorkflowStub(TransactionWorkflow.class, options);\n\n    // Execute UpdateWithStart and expect the exception\n    TransactionRequest txRequest =\n        new TransactionRequest(SOURCE_ACCOUNT, TARGET_ACCOUNT, INVALID_AMOUNT);\n    WorkflowUpdateException exception =\n        assertThrows(\n            WorkflowUpdateException.class,\n            () ->\n                WorkflowClient.executeUpdateWithStart(\n                    workflow::returnInitResult,\n                    UpdateOptions.<TxResult>newBuilder().build(),\n                    new WithStartWorkflowOperation<>(workflow::processTransaction, txRequest)));\n\n    // Verify the exception chain\n    assertTrue(exception.getCause() instanceof ActivityFailure);\n    ApplicationFailure appFailure = (ApplicationFailure) exception.getCause().getCause();\n    assertEquals(\"InvalidAmount\", appFailure.getType());\n    assertTrue(appFailure.getMessage().contains(\"Invalid Amount\"));\n\n    // Create a new stub to get the result\n    TransactionWorkflow workflowById =\n        workflowClient.newWorkflowStub(TransactionWorkflow.class, workflowId);\n    TxResult finalResult = WorkflowStub.fromTyped(workflowById).getResult(TxResult.class);\n    assertEquals(\"\", finalResult.getTransactionId());\n    assertEquals(\"Transaction cancelled.\", finalResult.getStatus());\n\n    // Verify activities were called in correct order\n    verify(activities).mintTransactionId(any());\n    verify(activities).initTransaction(any());\n    verify(activities).cancelTransaction(any());\n    verifyNoMoreInteractions(activities);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/encodefailures/EncodeFailuresTest.java",
    "content": "package io.temporal.samples.encodefailures;\n\nimport static org.junit.Assert.assertThrows;\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.api.common.v1.Payload;\nimport io.temporal.api.history.v1.HistoryEvent;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.CodecDataConverter;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport java.util.Collections;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class EncodeFailuresTest {\n\n  CodecDataConverter codecDataConverter =\n      new CodecDataConverter(\n          DefaultDataConverter.newDefaultInstance(),\n          Collections.singletonList(new SimplePrefixPayloadCodec()),\n          true);\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(\n              WorkflowImplementationOptions.newBuilder()\n                  .setFailWorkflowExceptionTypes(InvalidCustomerException.class)\n                  .build(),\n              CustomerAgeCheckImpl.class)\n          .setWorkflowClientOptions(\n              WorkflowClientOptions.newBuilder().setDataConverter(codecDataConverter).build())\n          .build();\n\n  @Test\n  public void testFailureMessageIsEncoded() {\n\n    CustomerAgeCheck workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                CustomerAgeCheck.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"CustomerAgeCheck\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .build());\n\n    assertThrows(\n        WorkflowFailedException.class,\n        () -> {\n          workflow.validateCustomer(new MyCustomer(\"John\", 20));\n        });\n\n    HistoryEvent wfExecFailedEvent =\n        testWorkflowRule.getWorkflowClient().fetchHistory(\"CustomerAgeCheck\").getLastEvent();\n    Payload payload =\n        wfExecFailedEvent\n            .getWorkflowExecutionFailedEventAttributes()\n            .getFailure()\n            .getEncodedAttributes();\n    assertTrue(isEncoded(payload));\n  }\n\n  private boolean isEncoded(Payload payload) {\n    return payload.getData().startsWith(SimplePrefixPayloadCodec.PREFIX);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/excludefrominterceptor/ExcludeFromInterceptorTest.java",
    "content": "package io.temporal.samples.excludefrominterceptor;\n\nimport io.temporal.api.enums.v1.EventType;\nimport io.temporal.api.history.v1.HistoryEvent;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.common.WorkflowExecutionHistory;\nimport io.temporal.samples.excludefrominterceptor.activities.ForInterceptorActivitiesImpl;\nimport io.temporal.samples.excludefrominterceptor.activities.MyActivitiesImpl;\nimport io.temporal.samples.excludefrominterceptor.interceptor.MyWorkerInterceptor;\nimport io.temporal.samples.excludefrominterceptor.workflows.*;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.util.Arrays;\nimport java.util.concurrent.CompletableFuture;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class ExcludeFromInterceptorTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(MyWorkflowOneImpl.class, MyWorkflowTwoImpl.class)\n          .setActivityImplementations(new MyActivitiesImpl(), new ForInterceptorActivitiesImpl())\n          .setWorkerFactoryOptions(\n              WorkerFactoryOptions.newBuilder()\n                  .setWorkerInterceptors(\n                      new MyWorkerInterceptor(\n                          // exclude MyWorkflowTwo from workflow interceptors\n                          Arrays.asList(MyWorkflowTwo.class.getSimpleName()),\n                          // exclude ActivityTwo and the \"ForInterceptor\" activities from activity\n                          // interceptor\n                          // note with SpringBoot starter you could use bean names here, we use\n                          // strings to\n                          // not have\n                          // to reflect on the activity impl class in sample\n                          Arrays.asList(\n                              \"ActivityTwo\",\n                              \"ForInterceptorActivityOne\",\n                              \"ForInterceptorActivityTwo\")))\n                  .build())\n          .build();\n\n  @Test\n  public void testExcludeFromInterceptor() {\n    MyWorkflow myWorkflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                MyWorkflowOne.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"MyWorkflowOne\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .build());\n\n    MyWorkflowTwo myWorkflowTwo =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                MyWorkflowTwo.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"MyWorkflowTwo\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .build());\n\n    WorkflowClient.start(myWorkflow::execute, \"my workflow input\");\n    WorkflowClient.start(myWorkflowTwo::execute, \"my workflow two input\");\n\n    // wait for both execs to complete\n    try {\n      CompletableFuture.allOf(\n              WorkflowStub.fromTyped(myWorkflow).getResultAsync(String.class),\n              WorkflowStub.fromTyped(myWorkflowTwo).getResultAsync(String.class))\n          .get();\n    } catch (Exception e) {\n      Assert.fail(\"Exception executing workflows: \" + e.getMessage());\n    }\n\n    Assert.assertEquals(5, getNumOfActivitiesForExec(\"MyWorkflowOne\"));\n    Assert.assertEquals(2, getNumOfActivitiesForExec(\"MyWorkflowTwo\"));\n  }\n\n  private int getNumOfActivitiesForExec(String workflowId) {\n    WorkflowExecutionHistory history =\n        testWorkflowRule.getWorkflowClient().fetchHistory(workflowId);\n    int counter = 0;\n    for (HistoryEvent event : history.getEvents()) {\n      if (event.getEventType().equals(EventType.EVENT_TYPE_ACTIVITY_TASK_COMPLETED)) {\n        counter++;\n      }\n    }\n    return counter;\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/fileprocessing/FileProcessingTest.java",
    "content": "package io.temporal.samples.fileprocessing;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.fileprocessing.StoreActivities.TaskQueueFileNamePair;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.Worker;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport org.junit.*;\n\npublic class FileProcessingTest {\n\n  private static final String HOST_NAME_1 = \"host1\";\n  private static final String HOST_NAME_2 = \"host2\";\n\n  private static final String FILE_NAME_UNPROCESSED = \"input_file\";\n  private static final String FILE_NAME_PROCESSED = \"output_file\";\n\n  private static final URL SOURCE;\n  private static final URL DESTINATION;\n\n  static {\n    try {\n      SOURCE = new URL(\"http://www.google.com/\");\n      DESTINATION = new URL(\"http://dummy\");\n    } catch (MalformedURLException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(FileProcessingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  // Host specific workers.\n  private Worker workerHost1;\n  private Worker workerHost2;\n\n  @Before\n  public void setUp() {\n    workerHost1 = testWorkflowRule.getTestEnvironment().newWorker(HOST_NAME_1);\n    workerHost2 = testWorkflowRule.getTestEnvironment().newWorker(HOST_NAME_2);\n  }\n\n  @Test\n  public void testHappyPath() {\n    StoreActivities activities = mock(StoreActivities.class);\n    when(activities.download(any()))\n        .thenReturn(new TaskQueueFileNamePair(HOST_NAME_1, FILE_NAME_UNPROCESSED));\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n\n    StoreActivities activitiesHost1 = mock(StoreActivities.class);\n    when(activitiesHost1.process(FILE_NAME_UNPROCESSED)).thenReturn(FILE_NAME_PROCESSED);\n    workerHost1.registerActivitiesImplementations(activitiesHost1);\n\n    StoreActivities activitiesHost2 = mock(StoreActivities.class);\n    workerHost2.registerActivitiesImplementations(activitiesHost2);\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    FileProcessingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                FileProcessingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    // Execute workflow waiting for completion.\n    workflow.processFile(SOURCE, DESTINATION);\n\n    verify(activities).download(SOURCE);\n    verify(activitiesHost1).process(FILE_NAME_UNPROCESSED);\n    verify(activitiesHost1).upload(FILE_NAME_PROCESSED, DESTINATION);\n\n    verifyNoMoreInteractions(activities, activitiesHost1);\n\n    verifyNoInteractions(activitiesHost2);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test(timeout = 30_000)\n  public void testHostFailover() {\n    StoreActivities activities = mock(StoreActivities.class);\n    when(activities.download(any()))\n        .thenReturn(new TaskQueueFileNamePair(HOST_NAME_1, FILE_NAME_UNPROCESSED))\n        .thenReturn(new TaskQueueFileNamePair(HOST_NAME_2, FILE_NAME_UNPROCESSED));\n\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n\n    StoreActivities activitiesHost1 = mock(StoreActivities.class);\n    when(activitiesHost1.process(FILE_NAME_UNPROCESSED))\n        .then(\n            invocation -> {\n              Thread.sleep(Long.MAX_VALUE);\n              return \"done\";\n            });\n\n    workerHost1.registerActivitiesImplementations(activitiesHost1);\n\n    StoreActivities activitiesHost2 = mock(StoreActivities.class);\n    when(activitiesHost2.process(FILE_NAME_UNPROCESSED)).thenReturn(FILE_NAME_PROCESSED);\n\n    workerHost2.registerActivitiesImplementations(activitiesHost2);\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    FileProcessingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                FileProcessingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    workflow.processFile(SOURCE, DESTINATION);\n\n    verify(activities, times(2)).download(SOURCE);\n\n    // TODO(maxim): Change to 1, once retry of SimulatedTimeoutException is not happening.\n    // https://github.com/temporalio/temporal-java-sdk/issues/94\n    verify(activitiesHost1, times(4)).process(FILE_NAME_UNPROCESSED);\n\n    verify(activitiesHost2).process(FILE_NAME_UNPROCESSED);\n    verify(activitiesHost2).upload(FILE_NAME_PROCESSED, DESTINATION);\n\n    verifyNoMoreInteractions(activities, activitiesHost1, activitiesHost2);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/getresultsasync/GetResultsAsyncTest.java",
    "content": "package io.temporal.samples.getresultsasync;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.concurrent.CompletableFuture;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class GetResultsAsyncTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setWorkflowTypes(MyWorkflowImpl.class).build();\n\n  @Test\n  public void testGetResultsAsync() throws Exception {\n\n    WorkflowStub workflowStub =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newUntypedWorkflowStub(\n                \"MyWorkflow\",\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    workflowStub.start(5);\n\n    CompletableFuture<String> completableFuture = workflowStub.getResultAsync(String.class);\n\n    String result = completableFuture.get();\n    assertNotNull(result);\n    assertEquals(\"woke up after 5 seconds\", result);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloAccumulatorTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static io.temporal.samples.hello.HelloAccumulator.MAX_AWAIT_TIME;\n\nimport io.temporal.client.BatchRequest;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloAccumulator.Greeting;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport java.util.ArrayDeque;\nimport java.util.HashSet;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloAccumulatorTest {\n\n  private TestWorkflowEnvironment testEnv;\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloAccumulator.AccumulatorWorkflowImpl.class)\n          .setActivityImplementations(new HelloAccumulator.GreetingActivitiesImpl())\n          .build();\n\n  @Test\n  public void testWorkflow() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    Greeting xvxGreeting = new Greeting(\"XVX Robot\", bucket, \"1123581321\");\n\n    workflow.sendGreeting(xvxGreeting);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert results.contains(\"XVX Robot\");\n  }\n\n  @Test\n  public void testJustExit() {\n    String bucket = \"blue\";\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    workflow.exit();\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (0)\");\n    assert !results.contains(\"Robot\");\n  }\n\n  @Test\n  public void testNoExit() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    Greeting xvxGreeting = new Greeting(\"XVX Robot\", bucket, \"1123581321\");\n\n    workflow.sendGreeting(xvxGreeting);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert results.contains(\"XVX Robot\");\n  }\n\n  @Test\n  public void testMultipleGreetings() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    workflow.sendGreeting(new Greeting(\"XVX Robot\", bucket, \"1123581321\"));\n    workflow.sendGreeting(new Greeting(\"Han Robot\", bucket, \"112358\"));\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (2)\");\n    assert results.contains(\"XVX Robot\");\n    assert results.contains(\"Han Robot\");\n  }\n\n  @Test\n  public void testDuplicateGreetings() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    workflow.sendGreeting(new Greeting(\"XVX Robot\", bucket, \"1123581321\"));\n    workflow.sendGreeting(new Greeting(\"Han Robot\", bucket, \"1123581321\"));\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert results.contains(\"XVX Robot\");\n    assert !results.contains(\"Han Robot\");\n  }\n\n  @Test\n  public void testWrongBucketGreeting() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloAccumulator.AccumulatorWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n\n    workflow.sendGreeting(new Greeting(\"Bad Robot\", \"orange\", \"1123581321\"));\n    workflow.sendGreeting(new Greeting(\"XVX Robot\", bucket, \"11235\"));\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert results.contains(\"XVX Robot\");\n    assert !results.contains(\"Bad Robot\");\n  }\n\n  @Test\n  public void testSignalWithStart() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    Greeting starterGreeting = new Greeting(\"Robby Robot\", bucket, \"112\");\n    BatchRequest request = client.newSignalWithStartRequest();\n    request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow::sendGreeting, starterGreeting);\n    client.signalWithStart(request);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert results.contains(\"Robby Robot\");\n  }\n\n  @Test\n  public void testWaitTooLongForFirstWorkflow() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting starterGreeting = new Greeting(\"Robby Robot\", bucket, \"112\");\n    BatchRequest request = client.newSignalWithStartRequest();\n    request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow::sendGreeting, starterGreeting);\n    client.signalWithStart(request);\n\n    // testEnv.sleep(MAX_AWAIT_TIME.plus(Duration.ofMillis(1))); is not long enough\n    // to guarantee the\n    // first workflow will end\n    testEnv.sleep(MAX_AWAIT_TIME.plus(Duration.ofMillis(100)));\n\n    HelloAccumulator.AccumulatorWorkflow workflow2 =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting secondGreeting = new Greeting(\"Dave Robot\", bucket, \"1123\");\n\n    request = client.newSignalWithStartRequest();\n    request.add(workflow2::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow2::sendGreeting, secondGreeting);\n    client.signalWithStart(request);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (1)\");\n    assert !results.contains(\"Robby Robot\");\n    assert results.contains(\"Dave Robot\");\n  }\n\n  @Test\n  public void testWaitNotLongEnoughForNewWorkflow() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting starterGreeting = new Greeting(\"Robby Robot\", bucket, \"112\");\n    BatchRequest request = client.newSignalWithStartRequest();\n    request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow::sendGreeting, starterGreeting);\n    client.signalWithStart(request);\n\n    testEnv.sleep(MAX_AWAIT_TIME.minus(Duration.ofMillis(1)));\n\n    HelloAccumulator.AccumulatorWorkflow workflow2 =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting secondGreeting = new Greeting(\"Dave Robot\", bucket, \"1123\");\n\n    request = client.newSignalWithStartRequest();\n    request.add(workflow2::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow2::sendGreeting, secondGreeting);\n    client.signalWithStart(request);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (2)\");\n    assert results.contains(\"Robby Robot\");\n    assert results.contains(\"Dave Robot\");\n  }\n\n  @Test\n  public void testWaitExactlyMAX_TIME() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting starterGreeting = new Greeting(\"Robby Robot\", bucket, \"112\");\n    BatchRequest request = client.newSignalWithStartRequest();\n    request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow::sendGreeting, starterGreeting);\n    client.signalWithStart(request);\n\n    testEnv.sleep(MAX_AWAIT_TIME);\n\n    HelloAccumulator.AccumulatorWorkflow workflow2 =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting secondGreeting = new Greeting(\"Dave Robot\", bucket, \"1123\");\n\n    request = client.newSignalWithStartRequest();\n    request.add(workflow2::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow2::sendGreeting, secondGreeting);\n    client.signalWithStart(request);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (2)\");\n    assert results.contains(\"Robby Robot\");\n    assert results.contains(\"Dave Robot\");\n  }\n\n  @Test\n  public void testSignalAfterExit() {\n    String bucket = \"blue\";\n\n    ArrayDeque<Greeting> greetingList = new ArrayDeque<Greeting>();\n    HashSet<String> allGreetingsSet = new HashSet<String>();\n    testEnv = testWorkflowRule.getTestEnvironment();\n    testEnv.start();\n\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    HelloAccumulator.AccumulatorWorkflow workflow =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting starterGreeting = new Greeting(\"Robby Robot\", bucket, \"112\");\n    BatchRequest request = client.newSignalWithStartRequest();\n    request.add(workflow::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow::sendGreeting, starterGreeting);\n    client.signalWithStart(request);\n\n    HelloAccumulator.AccumulatorWorkflow workflow2 =\n        client.newWorkflowStub(\n            HelloAccumulator.AccumulatorWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .setWorkflowId(bucket)\n                .setWorkflowId(\"helloacc-blue\")\n                .build());\n\n    Greeting secondGreeting = new Greeting(\"Dave Robot\", bucket, \"1123\");\n\n    request = client.newSignalWithStartRequest();\n    request.add(workflow2::accumulateGreetings, bucket, greetingList, allGreetingsSet);\n    request.add(workflow2::sendGreeting, secondGreeting);\n\n    // exit signal the workflow we signaled-to-start\n    workflow.exit();\n\n    // try to signal with start the workflow\n    client.signalWithStart(request);\n\n    String results = workflow.accumulateGreetings(bucket, greetingList, allGreetingsSet);\n    assert results.contains(\"Hello (2)\");\n    assert results.contains(\"Robby Robot\");\n    assert results.contains(\"Dave Robot\");\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityExclusiveChoiceJUnit5Test.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport io.temporal.testing.TestWorkflowExtension;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n/** Unit test for {@link HelloActivityExclusiveChoice}. Doesn't use an external Temporal service. */\npublic class HelloActivityExclusiveChoiceJUnit5Test {\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .setWorkflowTypes(HelloActivityExclusiveChoice.PurchaseFruitsWorkflowImpl.class)\n          .setActivityImplementations(new HelloActivityExclusiveChoice.OrderFruitsActivitiesImpl())\n          .build();\n\n  @Test\n  public void testWorkflow(HelloActivityExclusiveChoice.PurchaseFruitsWorkflow workflow) {\n    // Execute a workflow waiting for it to complete.\n    HelloActivityExclusiveChoice.ShoppingList shoppingList =\n        new HelloActivityExclusiveChoice.ShoppingList();\n    shoppingList.addFruitOrder(HelloActivityExclusiveChoice.Fruits.APPLE, 10);\n    StringBuilder orderResults = workflow.orderFruit(shoppingList);\n    assertEquals(\"Ordered 10 Apples...\", orderResults.toString());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityExclusiveChoiceTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static io.temporal.samples.hello.HelloActivityExclusiveChoice.WORKFLOW_ID;\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloActivityExclusiveChoice}. Doesn't use an external Temporal service. */\npublic class HelloActivityExclusiveChoiceTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloActivityExclusiveChoice.PurchaseFruitsWorkflowImpl.class)\n          .setActivityImplementations(new HelloActivityExclusiveChoice.OrderFruitsActivitiesImpl())\n          .build();\n\n  @Test\n  public void testWorkflow() {\n    // Get a workflow stub using the same task queue the worker uses.\n    HelloActivityExclusiveChoice.PurchaseFruitsWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloActivityExclusiveChoice.PurchaseFruitsWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(WORKFLOW_ID)\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .build());\n    // Execute a workflow waiting for it to complete.\n    HelloActivityExclusiveChoice.ShoppingList shoppingList =\n        new HelloActivityExclusiveChoice.ShoppingList();\n    shoppingList.addFruitOrder(HelloActivityExclusiveChoice.Fruits.APPLE, 10);\n    StringBuilder orderResults = workflow.orderFruit(shoppingList);\n    assertEquals(\"Ordered 10 Apples...\", orderResults.toString());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityJUnit5Test.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.assertThrows;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.samples.hello.HelloActivity.GreetingActivities;\nimport io.temporal.samples.hello.HelloActivity.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloActivity.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloActivity.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n/** Unit test for {@link HelloActivity}. Doesn't use an external Temporal service. */\npublic class HelloActivityJUnit5Test {\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl(\n      TestWorkflowEnvironment testEnv, Worker worker, GreetingWorkflow workflow) {\n    worker.registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testEnv.start();\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n  }\n\n  @Test\n  public void testMockedActivity(\n      TestWorkflowEnvironment testEnv, Worker worker, GreetingWorkflow workflow) {\n    // withoutAnnotations() is required to stop Mockito from copying\n    // method-level annotations from the GreetingActivities interface\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    worker.registerActivitiesImplementations(activities);\n    testEnv.start();\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n  }\n\n  @Test\n  public void testMockedActivityWithoutSettings(Worker worker) {\n    // Mocking activity that has method-level annotations\n    // with no withoutAnnotations() setting results in a failure\n    GreetingActivities activities = mock(GreetingActivities.class);\n    assertThrows(\n        IllegalArgumentException.class, () -> worker.registerActivitiesImplementations(activities));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityReplayTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.hamcrest.MatcherAssert.assertThat;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.internal.common.WorkflowExecutionHistory;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.testing.WorkflowReplayer;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.hamcrest.CoreMatchers;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Unit test for replay {@link HelloActivity.GreetingWorkflowImpl}. Doesn't use an external Temporal\n * service.\n */\npublic class HelloActivityReplayTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setDoNotStart(true).build();\n\n  @Test\n  public void replayWorkflowExecution() throws Exception {\n\n    final String eventHistory = executeWorkflow(HelloActivity.GreetingWorkflowImpl.class);\n\n    WorkflowReplayer.replayWorkflowExecution(\n        eventHistory, HelloActivity.GreetingWorkflowImpl.class);\n  }\n\n  @Test\n  public void replayWorkflowExecutionNonDeterministic() {\n\n    // We are executing the workflow with one implementation (GreetingWorkflowImplTest) and trying\n    // to replay the even history with a different implementation (GreetingWorkflowImpl),\n    // which causes an exception during the replay\n\n    try {\n\n      final String eventHistory = executeWorkflow(GreetingWorkflowImplTest.class);\n\n      WorkflowReplayer.replayWorkflowExecution(\n          eventHistory, HelloActivity.GreetingWorkflowImpl.class);\n\n      Assert.fail(\"Should have thrown an Exception\");\n    } catch (Exception e) {\n      assertThat(\n          e.getMessage(),\n          CoreMatchers.containsString(\"error=io.temporal.worker.NonDeterministicException\"));\n    }\n  }\n\n  private String executeWorkflow(\n      Class<? extends HelloActivity.GreetingWorkflow> workflowImplementationType) {\n\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new HelloActivity.GreetingActivitiesImpl());\n\n    testWorkflowRule.getWorker().registerWorkflowImplementationTypes(workflowImplementationType);\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    HelloActivity.GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloActivity.GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    WorkflowExecution execution = WorkflowStub.fromTyped(workflow).start(\"Hello\");\n    // wait until workflow completes\n    WorkflowStub.fromTyped(workflow).getResult(String.class);\n\n    return new WorkflowExecutionHistory(testWorkflowRule.getHistory(execution)).toJson(true);\n  }\n\n  public static class GreetingWorkflowImplTest implements HelloActivity.GreetingWorkflow {\n\n    private final HelloActivity.GreetingActivities activities =\n        Workflow.newActivityStub(\n            HelloActivity.GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public String getGreeting(String name) {\n      Workflow.sleep(100);\n      return activities.composeGreeting(\"Hello\", name);\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityRetryTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloActivityRetry.GreetingActivities;\nimport io.temporal.samples.hello.HelloActivityRetry.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloActivityRetry.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloActivityRetry.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.TestWatcher;\nimport org.junit.runner.Description;\n\n/** Unit test for {@link HelloActivityRetry}. Doesn't use an external Temporal service. */\npublic class HelloActivityRetryTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  /** Prints a history of the workflow under test in case of a test failure. */\n  @Rule\n  public TestWatcher watchman =\n      new TestWatcher() {\n        @Override\n        protected void failed(Throwable e, Description description) {\n          if (testWorkflowRule.getTestEnvironment() != null) {\n            System.err.println(testWorkflowRule.getTestEnvironment().getDiagnostics());\n            testWorkflowRule.getTestEnvironment().shutdown();\n          }\n        }\n      };\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    // Execute a workflow waiting for it to complete\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    GreetingActivities activities = mock(GreetingActivities.class);\n    when(activities.composeGreeting(\"Hello\", \"World\"))\n        .thenThrow(\n            new IllegalStateException(\"not yet1\"),\n            new IllegalStateException(\"not yet2\"),\n            new IllegalStateException(\"not yet3\"))\n        .thenReturn(\"Hello World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    verify(activities, times(4)).composeGreeting(anyString(), anyString());\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloActivityTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloActivity.GreetingActivities;\nimport io.temporal.samples.hello.HelloActivity.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloActivity.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloActivity.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloActivity}. Doesn't use an external Temporal service. */\npublic class HelloActivityTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    // withoutAnnotations() is required to stop Mockito from copying\n    // method-level annotations from the GreetingActivities interface\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test(expected = IllegalArgumentException.class)\n  public void testMockedActivityWithoutSettings() {\n    // Mocking activity that has method-level annotations\n    // with no withoutAnnotations() setting results in a failure\n    GreetingActivities activities = mock(GreetingActivities.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloAsyncActivityCompletionTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.ActivityCompletionClient;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloAsyncActivityCompletion.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloAsyncActivityCompletion.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloAsyncActivityCompletion.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloAsyncActivityCompletion}. Doesn't use an external Temporal service. */\npublic class HelloAsyncActivityCompletionTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() throws ExecutionException, InterruptedException {\n    ActivityCompletionClient completionClient =\n        testWorkflowRule.getWorkflowClient().newActivityCompletionClient();\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new GreetingActivitiesImpl(completionClient));\n    testWorkflowRule.getTestEnvironment().start();\n\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow asynchronously.\n    CompletableFuture<String> greeting = WorkflowClient.execute(workflow::getGreeting, \"World\");\n    // Wait for workflow completion.\n    assertEquals(\"Hello World!\", greeting.get());\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloAsyncLambdaTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloAsyncLambda.GreetingActivities;\nimport io.temporal.samples.hello.HelloAsyncLambda.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloAsyncLambda.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloAsyncLambda.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.Timeout;\n\n/** Unit test for {@link HelloAsyncLambda}. Doesn't use an external Temporal service. */\npublic class HelloAsyncLambdaTest {\n\n  @Rule public Timeout globalTimeout = Timeout.seconds(3);\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nHello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    GreetingActivities activities = mock(GreetingActivities.class);\n    when(activities.getGreeting()).thenReturn(\"Hello\");\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nHello World!\", greeting);\n\n    verify(activities, times(2)).composeGreeting(anyString(), anyString());\n    verify(activities, times(2)).getGreeting();\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloAsyncTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloAsync.GreetingActivities;\nimport io.temporal.samples.hello.HelloAsync.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloAsync.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloAsync.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloAsync}. Doesn't use an external Temporal service. */\npublic class HelloAsyncTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nBye World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    GreetingActivities activities = mock(GreetingActivities.class);\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    when(activities.composeGreeting(\"Bye\", \"World\")).thenReturn(\"Bye World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nBye World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloAwaitTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.fail;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.hello.HelloAwait.GreetingWorkflow;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloAwait}. Doesn't use an external Temporal service. */\npublic class HelloAwaitTest {\n\n  private final String WORKFLOW_ID = \"WORKFLOW1\";\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setWorkflowTypes(HelloAwait.GreetingWorkflowImpl.class).build();\n\n  @Test\n  public void testAwaitSignal() {\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(WORKFLOW_ID)\n            .build();\n\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously to not use another thread to await.\n    WorkflowClient.start(workflow::getGreeting);\n    workflow.waitForName(\"World\");\n    // So we can send a await to it using workflow stub immediately.\n    // But just to demonstrate the unit testing of a long running workflow adding a long sleep here.\n    //    testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(30));\n\n    WorkflowStub workflowById =\n        testWorkflowRule.getWorkflowClient().newUntypedWorkflowStub(WORKFLOW_ID);\n\n    String greeting = workflowById.getResult(String.class);\n    assertEquals(\"Hello World!\", greeting);\n  }\n\n  @Test\n  public void testAwaitTimeout() {\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(WORKFLOW_ID)\n            .build();\n\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously to not use another thread to wait.\n    WorkflowClient.start(workflow::getGreeting);\n\n    // Skip time to force Await timeout\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(30));\n\n    WorkflowStub workflowById =\n        testWorkflowRule.getWorkflowClient().newUntypedWorkflowStub(WORKFLOW_ID);\n\n    try {\n      workflowById.getResult(String.class);\n      fail(\"not reachable\");\n    } catch (WorkflowException e) {\n      ApplicationFailure applicationFailure = (ApplicationFailure) e.getCause();\n      assertEquals(\"signal-timeout\", applicationFailure.getType());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloCancellationScopeTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloCancellationScope.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloCancellationScope.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloCancellationScope.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloCancellationScope}. Doesn't use an external Temporal service. */\npublic class HelloCancellationScopeTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setActivityImplementations(new GreetingActivitiesImpl())\n          .build();\n\n  @Test(timeout = 240_000)\n  public void testActivityImpl() {\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertTrue(greeting.endsWith(\" World!\"));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloCancellationScopeWithTimerTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertTrue;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloCancellationScopeWithTimerTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloCancellationScopeWithTimer.CancellationWithTimerWorkflowImpl.class)\n          .setActivityImplementations(\n              new HelloCancellationScopeWithTimer.UpdateInfoActivitiesImpl())\n          .build();\n\n  @Test(timeout = 240_000)\n  public void testActivityImpl() {\n    // Get a workflow stub using the same task queue the worker uses.\n    HelloCancellationScopeWithTimer.CancellationWithTimerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloCancellationScopeWithTimer.CancellationWithTimerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String result = workflow.execute(\"Test Input\");\n    assertTrue(result.endsWith(\"Activity canceled due to timer firing.\"));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloChildJUnit5Test.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.Assert;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n/** Unit test for {@link HelloChild}. Doesn't use an external Temporal service. */\npublic class HelloChildJUnit5Test {\n  private HelloChild.GreetingChild child = mock(HelloChild.GreetingChildImpl.class);\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .setWorkflowTypes(HelloChild.GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testChild(\n      TestWorkflowEnvironment testEnv, Worker worker, HelloChild.GreetingWorkflow workflow) {\n    worker.registerWorkflowImplementationFactory(\n        HelloChild.GreetingChild.class,\n        () -> {\n          when(child.composeGreeting(anyString(), anyString())).thenReturn(\"Bye World!\");\n          return child;\n        });\n    testEnv.start();\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    Assert.assertEquals(\"Bye World!\", greeting);\n    verify(child).composeGreeting(eq(\"Hello\"), eq(\"World\"));\n\n    testEnv.shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloChildTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloChild.GreetingChild;\nimport io.temporal.samples.hello.HelloChild.GreetingChildImpl;\nimport io.temporal.samples.hello.HelloChild.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloChild.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.concurrent.atomic.AtomicReference;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloChild}. Doesn't use an external Temporal service. */\npublic class HelloChildTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setDoNotStart(true).build();\n\n  @Test\n  public void testChild() {\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationTypes(GreetingWorkflowImpl.class, GreetingChildImpl.class);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedChild() {\n    testWorkflowRule.getWorker().registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n\n    // As new mock is created on each workflow task the only last one is useful to verify calls.\n    AtomicReference<GreetingChild> lastChildMock = new AtomicReference<>();\n    // Factory is called to create a new workflow object on each workflow task.\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationFactory(\n            GreetingChild.class,\n            () -> {\n              GreetingChild child = mock(GreetingChild.class);\n              when(child.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n              lastChildMock.set(child);\n              return child;\n            });\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n    GreetingChild mock = lastChildMock.get();\n    verify(mock).composeGreeting(eq(\"Hello\"), eq(\"World\"));\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloCronTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static io.temporal.samples.hello.HelloCron.WORKFLOW_ID;\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloCron.GreetingActivities;\nimport io.temporal.samples.hello.HelloCron.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloCron.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloCron}. Doesn't use an external Temporal service. */\npublic class HelloCronTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testMockedActivity() {\n    GreetingActivities activities = mock(GreetingActivities.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Unfortunately the supported cron format of the Java test service is not exactly the same as\n    // the temporal service. For example @every is not supported by the unit testing framework.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setCronSchedule(\"0 * * * *\")\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(WORKFLOW_ID)\n            .build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Execute a workflow waiting for it to complete.\n    WorkflowExecution execution = WorkflowClient.start(workflow::greet, \"World\");\n    assertEquals(WORKFLOW_ID, execution.getWorkflowId());\n    // Use TestWorkflowEnvironment.sleep to execute the unit test without really sleeping.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofDays(1));\n    verify(activities, atLeast(10)).greet(anyString());\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloDelayedStartTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.WorkflowExecutionHistory;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloDelayedStartTest {\n  private final String WORKFLOW_ID = \"HelloDelayedStartWorkflow\";\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloDelayedStart.DelayedStartWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testDelayedStart() {\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(WORKFLOW_ID)\n            .setStartDelay(Duration.ofSeconds(2))\n            .build();\n\n    HelloDelayedStart.DelayedStartWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(HelloDelayedStart.DelayedStartWorkflow.class, workflowOptions);\n\n    workflow.start();\n\n    // Fetch event history and make sure we got the 2 seconds first workflow task backoff\n    WorkflowExecutionHistory history =\n        testWorkflowRule.getWorkflowClient().fetchHistory(WORKFLOW_ID);\n    com.google.protobuf.Duration backoff =\n        history\n            .getHistory()\n            .getEvents(0)\n            .getWorkflowExecutionStartedEventAttributes()\n            .getFirstWorkflowTaskBackoff();\n\n    assertEquals(2, backoff.getSeconds());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloDetachedCancellationScopeTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.TimeoutException;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Unit test for {@link HelloDetachedCancellationScope}. Doesn't use an external Temporal service.\n */\npublic class HelloDetachedCancellationScopeTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloDetachedCancellationScope.GreetingWorkflowImpl.class)\n          .setActivityImplementations(new HelloDetachedCancellationScope.GreetingActivitiesImpl())\n          .build();\n\n  @Test\n  public void testDetachedWorkflowScope() {\n    // Get a workflow stub using the same task queue the worker uses.\n    HelloDetachedCancellationScope.GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloDetachedCancellationScope.GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowClient.start(workflow::getGreeting, \"John\");\n\n    WorkflowStub workflowStub = WorkflowStub.fromTyped(workflow);\n\n    workflowStub.cancel();\n\n    String result;\n\n    try {\n      // Wait for workflow results\n      // Because we cancelled the workflow we should get WorkflowFailedException\n      result = workflowStub.getResult(6, TimeUnit.SECONDS, String.class, String.class);\n    } catch (TimeoutException | WorkflowFailedException e) {\n      // Query the workflow to get the result which was set by the detached cancellation scope run\n      result = workflowStub.query(\"queryGreeting\", String.class);\n    }\n    assertEquals(\"Goodbye John!\", result);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloDynamicActivityJUnit5Test.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.testing.TestActivityExtension;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n/**\n * Unit test for {@link HelloDynamic.DynamicGreetingActivityImpl}. Doesn't use an external Temporal\n * service.\n */\npublic class HelloDynamicActivityJUnit5Test {\n\n  @RegisterExtension\n  public static final TestActivityExtension testActivityExtension =\n      TestActivityExtension.newBuilder()\n          .setActivityImplementations(new HelloDynamic.DynamicGreetingActivityImpl())\n          .build();\n\n  /**\n   * Dynamic activity {@link HelloDynamic.DynamicGreetingActivityImpl} is injected as an\n   * implementation for a static activity interface {@link MyStaticActivity}.\n   */\n  @Test\n  public void testDynamicActivity(MyStaticActivity activity) {\n    String result = activity.response(\"Hello\", \"John\", \"HelloDynamicActivityJUnit5Test\");\n\n    assertEquals(\"MyStaticResponse: Hello John from: HelloDynamicActivityJUnit5Test\", result);\n  }\n\n  @ActivityInterface(namePrefix = \"MyStatic\")\n  public interface MyStaticActivity {\n    String response(String name, String greeting, String source);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloDynamicTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloDynamic}. Doesn't use an external Temporal service. */\npublic class HelloDynamicTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloDynamic.DynamicGreetingWorkflowImpl.class)\n          .setActivityImplementations(new HelloDynamic.DynamicGreetingActivityImpl())\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(HelloDynamic.WORKFLOW_ID)\n            .build();\n\n    WorkflowStub workflow =\n        testWorkflowRule.getWorkflowClient().newUntypedWorkflowStub(\"DynamicWF\", workflowOptions);\n\n    // Start workflow execution and signal right after Pass in the workflow args and signal args\n    workflow.signalWithStart(\"greetingSignal\", new Object[] {\"John\"}, new Object[] {\"Hello\"});\n\n    // Wait for workflow to finish getting the results\n    String result = workflow.getResult(String.class);\n\n    assertNotNull(result);\n    assertEquals(\"DynamicACT: Hello John from: DynamicWF\", result);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloEagerWorkflowStartTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingActivities;\nimport io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingLocalActivitiesImpl;\nimport io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloEagerWorkflowStart.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloEagerWorkflowStart}. Doesn't use an external Temporal service. */\npublic class HelloEagerWorkflowStartTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new GreetingLocalActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    // withoutAnnotations() is required to stop Mockito from copying\n    // method-level annotations from the GreetingActivities interface\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloExceptionTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport io.temporal.api.enums.v1.TimeoutType;\nimport io.temporal.client.WorkflowException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.failure.ChildWorkflowFailure;\nimport io.temporal.failure.TimeoutFailure;\nimport io.temporal.samples.hello.HelloException.GreetingChildImpl;\nimport io.temporal.samples.hello.HelloException.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloException.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloExceptionTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setDoNotStart(true).build();\n\n  @Test\n  public void testIOException() {\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationTypes(\n            HelloException.GreetingWorkflowImpl.class, GreetingChildImpl.class);\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new HelloException.GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    try {\n      workflow.getGreeting(\"World\");\n      throw new IllegalStateException(\"unreachable\");\n    } catch (WorkflowException e) {\n      assertTrue(e.getCause() instanceof ChildWorkflowFailure);\n      assertTrue(e.getCause().getCause() instanceof ActivityFailure);\n      assertTrue(e.getCause().getCause().getCause() instanceof ApplicationFailure);\n      assertEquals(\n          \"Hello World!\",\n          ((ApplicationFailure) e.getCause().getCause().getCause()).getOriginalMessage());\n    }\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testActivityScheduleToStartTimeout() {\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationTypes(\n            HelloException.GreetingWorkflowImpl.class, GreetingChildImpl.class);\n\n    // We don't register an activity implementation on the worker and the activity has 5 seconds\n    // schedule to start timeout in GreetingChildImpl\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n    try {\n      workflow.getGreeting(\"World\");\n      throw new IllegalStateException(\"unreachable\");\n    } catch (WorkflowException e) {\n      assertTrue(e.getCause() instanceof ChildWorkflowFailure);\n      Throwable doubleCause = e.getCause().getCause();\n      assertTrue(doubleCause instanceof ActivityFailure);\n      Throwable tripleCause = doubleCause.getCause();\n      assertTrue(tripleCause instanceof TimeoutFailure);\n      assertEquals(\n          TimeoutType.TIMEOUT_TYPE_SCHEDULE_TO_START,\n          ((TimeoutFailure) tripleCause).getTimeoutType());\n    }\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test(timeout = 100000)\n  public void testChildWorkflowTimeout() {\n    testWorkflowRule.getWorker().registerWorkflowImplementationTypes(GreetingWorkflowImpl.class);\n    // Mock a child that times out.\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationFactory(\n            HelloException.GreetingChild.class,\n            () -> {\n              GreetingChildImpl child = mock(GreetingChildImpl.class);\n              when(child.composeGreeting(anyString(), anyString()))\n                  .thenThrow(\n                      new TimeoutFailure(\n                          \"simulated\", null, TimeoutType.TIMEOUT_TYPE_START_TO_CLOSE));\n              return child;\n            });\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n    try {\n      workflow.getGreeting(\"World\");\n      throw new IllegalStateException(\"unreachable\");\n    } catch (WorkflowException e) {\n      assertTrue(e.getCause() instanceof ChildWorkflowFailure);\n      assertTrue(e.getCause().getCause() instanceof TimeoutFailure);\n    }\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloLocalActivityTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloLocalActivity.GreetingActivities;\nimport io.temporal.samples.hello.HelloLocalActivity.GreetingLocalActivityImpl;\nimport io.temporal.samples.hello.HelloLocalActivity.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloLocalActivity.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloLocalActivity}. Doesn't use an external Temporal service. */\npublic class HelloLocalActivityTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingLocalActivityImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    // withoutAnnotations() is required to stop Mockito from copying\n    // method-level annotations from the GreetingActivities interface\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloParallelActivityTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.Arrays;\nimport java.util.List;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloParallelActivity}. Doesn't use an external Temporal service. */\npublic class HelloParallelActivityTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloParallelActivity.MultiGreetingWorkflowImpl.class)\n          .setActivityImplementations(new HelloParallelActivity.GreetingActivitiesImpl())\n          .build();\n\n  @Test\n  public void testParallelActivity() {\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n\n    HelloParallelActivity.MultiGreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(HelloParallelActivity.MultiGreetingWorkflow.class, workflowOptions);\n    // Execute a workflow waiting for it to complete.\n    List<String> results =\n        workflow.getGreetings(Arrays.asList(\"John\", \"Marry\", \"Michael\", \"Janet\"));\n    assertEquals(4, results.size());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloPeriodicTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static io.temporal.samples.hello.HelloPeriodic.*;\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertTrue;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.WorkflowExecutionStatus;\nimport io.temporal.api.filter.v1.WorkflowExecutionFilter;\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsRequest;\nimport io.temporal.api.workflowservice.v1.ListClosedWorkflowExecutionsResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloPeriodic.GreetingActivities;\nimport io.temporal.samples.hello.HelloPeriodic.GreetingActivitiesImpl;\nimport io.temporal.samples.hello.HelloPeriodic.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloPeriodic.GreetingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloPeriodic}. Doesn't use an external Temporal service. */\npublic class HelloPeriodicTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testPeriodicActivityInvocation() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowId(WORKFLOW_ID)\n                    .build());\n    // Execute a workflow waiting for it to complete.\n    WorkflowExecution execution = WorkflowClient.start(workflow::greetPeriodically, \"World\");\n    assertEquals(WORKFLOW_ID, execution.getWorkflowId());\n    // Validate that workflow was continued as new at least once.\n    // Use TestWorkflowEnvironment.sleep to execute the unit test without really sleeping.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofMinutes(3));\n    ListClosedWorkflowExecutionsRequest request =\n        ListClosedWorkflowExecutionsRequest.newBuilder()\n            .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n            .setExecutionFilter(WorkflowExecutionFilter.newBuilder().setWorkflowId(WORKFLOW_ID))\n            .build();\n    ListClosedWorkflowExecutionsResponse listResponse =\n        testWorkflowRule\n            .getTestEnvironment()\n            .getWorkflowService()\n            .blockingStub()\n            .listClosedWorkflowExecutions(request);\n    assertTrue(listResponse.getExecutionsCount() > 1);\n    for (WorkflowExecutionInfo e : listResponse.getExecutionsList()) {\n      assertEquals(\n          WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_CONTINUED_AS_NEW, e.getStatus());\n    }\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    GreetingActivities activities = mock(GreetingActivities.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowId(WORKFLOW_ID)\n                    .build());\n    // Execute a workflow waiting for it to complete.\n    WorkflowExecution execution = WorkflowClient.start(workflow::greetPeriodically, \"World\");\n    assertEquals(WORKFLOW_ID, execution.getWorkflowId());\n    // Use TestWorkflowEnvironment.sleep to execute the unit test without really sleeping.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofMinutes(1));\n    verify(activities, atLeast(5)).greet(anyString());\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloPolymorphicActivityTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloPolymorphicActivity.ByeActivityImpl;\nimport io.temporal.samples.hello.HelloPolymorphicActivity.GreetingWorkflow;\nimport io.temporal.samples.hello.HelloPolymorphicActivity.GreetingWorkflowImpl;\nimport io.temporal.samples.hello.HelloPolymorphicActivity.HelloActivityImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloActivity}. Doesn't use an external Temporal service. */\npublic class HelloPolymorphicActivityTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new HelloActivityImpl(), new ByeActivityImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nBye World!\\n\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    HelloPolymorphicActivity.HelloActivity hello =\n        mock(HelloPolymorphicActivity.HelloActivity.class);\n    when(hello.composeGreeting(\"World\")).thenReturn(\"Hello World!\");\n    HelloPolymorphicActivity.ByeActivity bye = mock(HelloPolymorphicActivity.ByeActivity.class);\n    when(bye.composeGreeting(\"World\")).thenReturn(\"Bye World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(hello, bye);\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                GreetingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.getGreeting(\"World\");\n    assertEquals(\"Hello World!\\nBye World!\\n\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloQueryTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloQuery.GreetingWorkflow;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloQuery}. Doesn't use an external Temporal service. */\npublic class HelloQueryTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setWorkflowTypes(HelloQuery.GreetingWorkflowImpl.class).build();\n\n  @Test(timeout = 5000)\n  public void testQuery() {\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously to not use another thread to query.\n    WorkflowClient.start(workflow::createGreeting, \"World\");\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send a signal to it using workflow stub.\n    assertEquals(\"Hello World!\", workflow.queryGreeting());\n\n    // Unit tests should call testWorkflowRule.getTestEnvironment().sleep.\n    // It allows skipping the time if workflow is just waiting on a timer\n    // and executing tests of long running workflows very fast.\n    // Note that this unit test executes under a second and not\n    // over 3 as it would if Thread.sleep(3000) was called.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(3));\n\n    assertEquals(\"Bye World!\", workflow.queryGreeting());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloSearchAttributesTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static io.temporal.samples.hello.HelloSearchAttributes.getKeywordFromSearchAttribute;\n\nimport io.temporal.api.common.v1.SearchAttributes;\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.Map;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloSearchAttributes}. Doesn't use an external Temporal service. */\npublic class HelloSearchAttributesTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloSearchAttributes.GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testStartWorkflowWithSearchAttribute() {\n\n    final String taskQueue = testWorkflowRule.getTaskQueue();\n    final String workflowId = \"workflowId\";\n    final String customKeywordField = \"CustomKeywordField\";\n    final String customKeywordValue = \"CustomKeywordValue\";\n\n    testWorkflowRule\n        .getWorker()\n        .registerActivitiesImplementations(new HelloSearchAttributes.GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Get a workflow stub using the same task queue the worker uses.\n    final HelloSearchAttributes.GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloSearchAttributes.GreetingWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setSearchAttributes(Map.of(customKeywordField, customKeywordValue))\n                    .setWorkflowId(workflowId)\n                    .setTaskQueue(taskQueue)\n                    .build());\n\n    final WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    final WorkflowExecution execution = untyped.start(\"Hello\");\n\n    // wait until workflow completes\n    untyped.getResult(String.class);\n\n    final DescribeWorkflowExecutionResponse resp =\n        testWorkflowRule\n            .getWorkflowClient()\n            .getWorkflowServiceStubs()\n            .blockingStub()\n            .describeWorkflowExecution(\n                DescribeWorkflowExecutionRequest.newBuilder()\n                    .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n                    .setExecution(execution)\n                    .build());\n    // get all search attributes\n    final SearchAttributes searchAttributes = resp.getWorkflowExecutionInfo().getSearchAttributes();\n\n    Assert.assertEquals(\n        customKeywordValue, getKeywordFromSearchAttribute(searchAttributes, customKeywordField));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloSideEffectTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloSideEffectTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloSideEffect.SideEffectWorkflowImpl.class)\n          .setActivityImplementations(new HelloSideEffect.SideEffectActivitiesImpl())\n          .build();\n\n  @Test\n  public void testSideffectsWorkflow() {\n    // Get a workflow stub using the same task queue the worker uses.\n    HelloSideEffect.SideEffectWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloSideEffect.SideEffectWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    // Execute a workflow waiting for it to complete.\n    String result = workflow.execute();\n    // make sure the result is same as the query result after workflow completion\n    assertEquals(result, workflow.getResult());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloSignalTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.api.enums.v1.WorkflowIdReusePolicy;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloSignal.GreetingWorkflow;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport java.util.List;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloSignal}. Doesn't use an external Temporal service. */\npublic class HelloSignalTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloSignal.GreetingWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testSignal() {\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowIdReusePolicy(\n                WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE)\n            .build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously to not use another thread to signal.\n    WorkflowClient.start(workflow::getGreetings);\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send a signal to it using workflow stub immediately.\n    // But just to demonstrate the unit testing of a long running workflow adding a long sleep here.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofDays(1));\n    // This workflow keeps receiving signals until exit is called\n    workflow.waitForName(\"World\");\n    workflow.waitForName(\"Universe\");\n    workflow.exit();\n    // Calling synchronous getGreeting after workflow has started reconnects to the existing\n    // workflow and\n    // blocks until result is available. Note that this behavior assumes that WorkflowOptions are\n    // not configured\n    // with WorkflowIdReusePolicy.AllowDuplicate. In that case the call would fail with\n    // WorkflowExecutionAlreadyStartedException.\n    List<String> greetings = workflow.getGreetings();\n    assertEquals(2, greetings.size());\n    assertEquals(\"Hello World!\", greetings.get(0));\n    assertEquals(\"Hello Universe!\", greetings.get(1));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloSignalWithStartAndWorkflowInitTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.junit.jupiter.api.Assertions.fail;\n\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\npublic class HelloSignalWithStartAndWorkflowInitTest {\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          .registerWorkflowImplementationTypes(\n              HelloSignalWithStartAndWorkflowInit.WithInitMyWorkflowImpl.class)\n          .registerWorkflowImplementationTypes(\n              WorkflowImplementationOptions.newBuilder()\n                  .setFailWorkflowExceptionTypes(NullPointerException.class)\n                  .build(),\n              HelloSignalWithStartAndWorkflowInit.WithoutInitMyWorkflowImpl.class)\n          .setActivityImplementations(\n              new HelloSignalWithStartAndWorkflowInit.MyGreetingActivitiesImpl())\n          .build();\n\n  @Test\n  public void testWithInit(TestWorkflowEnvironment testEnv, Worker worker) {\n    HelloSignalWithStartAndWorkflowInit.MyWorkflowWithInit withInitStub =\n        testEnv\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloSignalWithStartAndWorkflowInit.MyWorkflowWithInit.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"with-init\")\n                    .setTaskQueue(worker.getTaskQueue())\n                    .build());\n    WorkflowStub.fromTyped(withInitStub)\n        .signalWithStart(\n            \"addGreeting\",\n            new Object[] {new HelloSignalWithStartAndWorkflowInit.Person(\"Michael\", \"Jordan\", 55)},\n            new Object[] {new HelloSignalWithStartAndWorkflowInit.Person(\"John\", \"Stockton\", 57)});\n    String result = WorkflowStub.fromTyped(withInitStub).getResult(String.class);\n    assertEquals(\"Hello Michael Jordan,Hello John Stockton\", result);\n  }\n\n  @Test\n  public void testWithoutInit(TestWorkflowEnvironment testEnv, Worker worker) {\n    HelloSignalWithStartAndWorkflowInit.MyWorkflowNoInit noInitStub =\n        testEnv\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloSignalWithStartAndWorkflowInit.MyWorkflowNoInit.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"without-init\")\n                    .setTaskQueue(worker.getTaskQueue())\n                    .build());\n    WorkflowStub.fromTyped(noInitStub)\n        .signalWithStart(\n            \"addGreeting\",\n            new Object[] {new HelloSignalWithStartAndWorkflowInit.Person(\"Michael\", \"Jordan\", 55)},\n            new Object[] {new HelloSignalWithStartAndWorkflowInit.Person(\"John\", \"Stockton\", 57)});\n    try {\n      WorkflowStub.fromTyped(noInitStub).getResult(String.class);\n      fail(\"Workflow execution should have failed\");\n    } catch (Exception e) {\n      if (!(e instanceof WorkflowFailedException)) {\n        fail(\"Workflow execution should have failed with WorkflowFailedException\");\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloSignalWithTimerTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.WorkflowExecutionStatus;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloSignalWithTimerTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloSignalWithTimer.SignalWithTimerWorkflowImpl.class)\n          .setActivityImplementations(new HelloSignalWithTimer.ValueProcessingActivitiesImpl())\n          .build();\n\n  private static final String WORKFLOW_ID = \"SignalWithTimerTestWorkflow\";\n\n  @Test\n  public void testSignalWithTimer() {\n    HelloSignalWithTimer.SignalWithTimerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloSignalWithTimer.SignalWithTimerWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowId(WORKFLOW_ID)\n                    .build());\n\n    WorkflowClient.start(workflow::execute);\n    workflow.newValue(\"1\");\n    workflow.newValue(\"2\");\n    workflow.exit();\n\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n\n    DescribeWorkflowExecutionResponse res =\n        testWorkflowRule\n            .getWorkflowClient()\n            .getWorkflowServiceStubs()\n            .blockingStub()\n            .describeWorkflowExecution(\n                DescribeWorkflowExecutionRequest.newBuilder()\n                    .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n                    .setExecution(WorkflowExecution.newBuilder().setWorkflowId(WORKFLOW_ID).build())\n                    .build());\n\n    Assert.assertEquals(\n        res.getWorkflowExecutionInfo().getStatus(),\n        WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_COMPLETED);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloStandaloneActivityTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloStandaloneActivity.GreetingActivities;\nimport io.temporal.samples.hello.HelloStandaloneActivity.GreetingActivitiesImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Unit tests for {@link HelloStandaloneActivity}. Uses an embedded Temporal test server so no\n * external service is required.\n *\n * <p>Standalone Activities do not use a Workflow at runtime, but the embedded test server only\n * supports Activity execution through a Workflow. These tests therefore drive the Activity through\n * a minimal wrapper Workflow so the Activity logic is exercised against the real SDK worker stack.\n */\npublic class HelloStandaloneActivityTest {\n\n  /**\n   * Minimal wrapper Workflow used to invoke {@link GreetingActivities} through the embedded test\n   * worker.\n   */\n  @WorkflowInterface\n  public interface TestWorkflow {\n\n    @WorkflowMethod\n    String run(String greeting, String name);\n  }\n\n  public static class TestWorkflowImpl implements TestWorkflow {\n\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build());\n\n    @Override\n    public String run(String greeting, String name) {\n      return activities.composeGreeting(greeting, name);\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(TestWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"Hello, World!\", workflow.run(\"Hello\", \"World\"));\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    // withoutAnnotations() prevents Mockito from copying @ActivityMethod from the interface onto\n    // the mock, which would cause worker registration to fail.\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello, World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"Hello, World!\", workflow.run(\"Hello\", \"World\"));\n    verify(activities).composeGreeting(\"Hello\", \"World\");\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloUpdateAndCancellationTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.activity.*;\nimport io.temporal.api.enums.v1.WorkflowIdConflictPolicy;\nimport io.temporal.client.*;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.CanceledFailure;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.workflow.*;\nimport java.time.Duration;\nimport java.util.concurrent.TimeUnit;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloUpdateAndCancellationTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(TestWorkflowImpl.class)\n          .setActivityImplementations(new TestActivitiesImpl())\n          .build();\n\n  @Test\n  public void testUpdateAndWorkflowCancellation() {\n    // Start workflow with UpdateWithStart then cancel workflow before activity completes\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"test-workflow-cancel\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowIdConflictPolicy(\n                        WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING)\n                    .build());\n\n    WorkflowUpdateHandle<String> updateHandle =\n        WorkflowClient.startUpdateWithStart(\n            workflow::mileStoneCompleted,\n            UpdateOptions.<String>newBuilder()\n                .setWaitForStage(WorkflowUpdateStage.ACCEPTED)\n                .build(),\n            new WithStartWorkflowOperation<>(workflow::execute));\n\n    testWorkflowRule\n        .getTestEnvironment()\n        .registerDelayedCallback(\n            Duration.ofSeconds(3),\n            () -> {\n              WorkflowStub.fromTyped(workflow).cancel(\"canceled by test\");\n            });\n\n    String updateResult = updateHandle.getResult();\n    Assert.assertEquals(\"milestone canceled\", updateResult);\n\n    try {\n      WorkflowStub.fromTyped(workflow).getResult(String.class);\n      Assert.fail(\"Workflow Execution should have been canceled\");\n    } catch (WorkflowFailedException e) {\n      // Our workflow should have been canceled\n      Assert.assertEquals(CanceledFailure.class, e.getCause().getClass());\n    }\n  }\n\n  @Test\n  public void testUpdateAndActivityCancellation() {\n    // Start workflow with UpdateWithStart then cancel the activity only by sending signal to\n    // execution\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"test-activity-cancel\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .setWorkflowIdConflictPolicy(\n                        WorkflowIdConflictPolicy.WORKFLOW_ID_CONFLICT_POLICY_USE_EXISTING)\n                    .build());\n\n    WorkflowUpdateHandle<String> updateHandle =\n        WorkflowClient.startUpdateWithStart(\n            workflow::mileStoneCompleted,\n            UpdateOptions.<String>newBuilder()\n                .setWaitForStage(WorkflowUpdateStage.ACCEPTED)\n                .build(),\n            new WithStartWorkflowOperation<>(workflow::execute));\n\n    testWorkflowRule\n        .getTestEnvironment()\n        .registerDelayedCallback(\n            Duration.ofSeconds(3),\n            () -> {\n              WorkflowStub.fromTyped(workflow).signal(\"cancelActivity\");\n            });\n\n    String updateResult = updateHandle.getResult();\n    Assert.assertEquals(\"milestone canceled\", updateResult);\n\n    try {\n      WorkflowStub.fromTyped(workflow).getResult(String.class);\n      Assert.fail(\"Workflow Execution should have failed\");\n    } catch (WorkflowFailedException e) {\n      // In this case we did not cancel workflow execution but we failed it by throwing\n      // ActivityFailure\n      Assert.assertEquals(ActivityFailure.class, e.getCause().getClass());\n      ActivityFailure af = (ActivityFailure) e.getCause();\n      // Since we canceled the activity still, the cause of ActivityFailure should be\n      // CanceledFailure\n      Assert.assertEquals(CanceledFailure.class, af.getCause().getClass());\n    }\n  }\n\n  @WorkflowInterface\n  public interface TestWorkflow {\n    @WorkflowMethod\n    String execute();\n\n    @UpdateMethod\n    String mileStoneCompleted();\n\n    @SignalMethod\n    void cancelActivity();\n  }\n\n  public static class TestWorkflowImpl implements TestWorkflow {\n    boolean milestoneDone, mileStoneCanceled;\n    CancellationScope scope;\n    TestActivities activities =\n        Workflow.newActivityStub(\n            TestActivities.class,\n            ActivityOptions.newBuilder()\n                .setHeartbeatTimeout(Duration.ofSeconds(3))\n                .setStartToCloseTimeout(Duration.ofSeconds(10))\n                .setCancellationType(ActivityCancellationType.WAIT_CANCELLATION_COMPLETED)\n                .build());\n\n    @Override\n    public String execute() {\n      scope =\n          Workflow.newCancellationScope(\n              () -> {\n                activities.runActivity();\n              });\n\n      try {\n        scope.run();\n        milestoneDone = true;\n        Workflow.await(Workflow::isEveryHandlerFinished);\n        return \"workflow completed\";\n      } catch (ActivityFailure e) {\n        if (e.getCause() instanceof CanceledFailure) {\n          CancellationScope detached =\n              Workflow.newDetachedCancellationScope(\n                  () -> {\n                    mileStoneCanceled = true;\n                    Workflow.await(Workflow::isEveryHandlerFinished);\n                  });\n          detached.run();\n        }\n        throw e;\n      }\n    }\n\n    @Override\n    public String mileStoneCompleted() {\n      Workflow.await(() -> milestoneDone || mileStoneCanceled);\n      // For sake of testing isEveryHandlerFinished block here for 2 seconds\n      Workflow.sleep(Duration.ofSeconds(2));\n      return milestoneDone ? \"milestone completed\" : \"milestone canceled\";\n    }\n\n    @Override\n    public void cancelActivity() {\n      if (scope != null) {\n        scope.cancel(\"test reason\");\n      }\n    }\n  }\n\n  @ActivityInterface\n  public interface TestActivities {\n    void runActivity();\n  }\n\n  public static class TestActivitiesImpl implements TestActivities {\n\n    @Override\n    public void runActivity() {\n      ActivityExecutionContext context = Activity.getExecutionContext();\n      for (int i = 0; i < 9; i++) {\n        sleep(1);\n        try {\n          context.heartbeat(i);\n        } catch (ActivityCompletionException e) {\n          throw e;\n        }\n      }\n    }\n  }\n\n  private static void sleep(int seconds) {\n    try {\n      Thread.sleep(TimeUnit.SECONDS.toMillis(seconds));\n    } catch (InterruptedException e) {\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloUpdateTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.api.enums.v1.WorkflowIdReusePolicy;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.hello.HelloUpdate.GreetingWorkflow;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport java.util.List;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/** Unit test for {@link HelloUpdateTest}. Doesn't use an external Temporal service. */\npublic class HelloUpdateTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(HelloUpdate.GreetingWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testUpdate() {\n    // Setup mocks\n    HelloActivity.GreetingActivities activities =\n        mock(HelloActivity.GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello World!\");\n    when(activities.composeGreeting(\"Hello\", \"Universe\")).thenReturn(\"Hello Universe!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n    // Get a workflow stub using the same task queue the worker uses.\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowIdReusePolicy(\n                WorkflowIdReusePolicy.WORKFLOW_ID_REUSE_POLICY_REJECT_DUPLICATE)\n            .build();\n    GreetingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(GreetingWorkflow.class, workflowOptions);\n\n    // Start workflow asynchronously to not use another thread to update.\n    WorkflowClient.start(workflow::getGreetings);\n\n    // After start for getGreeting returns, the workflow is guaranteed to be started.\n    // So we can send a update to it using workflow stub immediately.\n    // But just to demonstrate the unit testing of a long running workflow adding a long sleep here.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofDays(1));\n    // This workflow keeps receiving updates until exit is called\n    assertEquals(1, workflow.addGreeting(\"World\"));\n    assertEquals(2, workflow.addGreeting(\"Universe\"));\n    workflow.exit();\n    // Calling synchronous getGreeting after workflow has started reconnects to the existing\n    // workflow and\n    // blocks until result is available. Note that this behavior assumes that WorkflowOptions are\n    // not configured\n    // with WorkflowIdReusePolicy.AllowDuplicate. In that case the call would fail with\n    // WorkflowExecutionAlreadyStartedException.\n    List<String> greetings = workflow.getGreetings();\n    assertEquals(2, greetings.size());\n    assertEquals(\"Hello World!\", greetings.get(0));\n    assertEquals(\"Hello Universe!\", greetings.get(1));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/hello/HelloWorkflowTimerTest.java",
    "content": "package io.temporal.samples.hello;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class HelloWorkflowTimerTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(\n              HelloWorkflowTimer.WorkflowWithTimerImpl.class,\n              HelloWorkflowTimer.WorkflowWithTimerChildWorkflowImpl.class)\n          .setActivityImplementations(new HelloWorkflowTimer.WorkflowWithTimerActivitiesImpl())\n          .build();\n\n  @Test\n  public void testWorkflowTimer() {\n    HelloWorkflowTimer.WorkflowWithTimer workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloWorkflowTimer.WorkflowWithTimer.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"WorkflowWithTimerTestId\")\n                    .setTaskQueue(testWorkflowRule.getTaskQueue())\n                    .build());\n\n    String result = workflow.execute(\"test input\");\n    Assert.assertEquals(\"Workflow timer fired while activities were executing.\", result);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/interceptorreplaytest/InterceptorReplayTest.java",
    "content": "package io.temporal.samples.interceptorreplaytest;\n\nimport static org.junit.Assert.fail;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.WorkflowExecutionHistory;\nimport io.temporal.common.interceptors.*;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.testing.WorkflowReplayer;\nimport io.temporal.worker.Worker;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.time.Duration;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\npublic class InterceptorReplayTest {\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          // Register workflow and activity impls\n          .registerWorkflowImplementationTypes(TestWorkflowImpl.class)\n          .setActivityImplementations(new TestActivitiesImpl())\n          // Register worker interceptor\n          .setWorkerFactoryOptions(\n              WorkerFactoryOptions.newBuilder()\n                  .setWorkerInterceptors(new TestWorkerInterceptor())\n                  .build())\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testReplayWithInterceptors(TestWorkflowEnvironment testEnv, Worker worker) {\n    // Run our test workflow. We need to set workflow id so can get history after\n    testEnv.start();\n    TestWorkflow workflow =\n        testEnv\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder()\n                    .setWorkflowId(\"test-workflow\")\n                    .setTaskQueue(worker.getTaskQueue())\n                    .build());\n    workflow.execute();\n\n    // Replay execution with history of just executed\n    WorkflowExecutionHistory eventHistory =\n        testEnv.getWorkflowClient().fetchHistory(\"test-workflow\");\n\n    try {\n      WorkflowReplayer.replayWorkflowExecution(eventHistory, worker);\n    } catch (Exception e) {\n      fail(e.getMessage());\n    }\n    testEnv.shutdown();\n\n    // Try replaying execution with test env where we dont have interceptors registered\n    TestWorkflowEnvironment testEnv2 = TestWorkflowEnvironment.newInstance();\n    Worker testEnv2Worker = testEnv2.newWorker(\"test-taskqueue\");\n    testEnv2Worker.registerWorkflowImplementationTypes(TestWorkflowImpl.class);\n    testEnv2Worker.registerActivitiesImplementations(new TestActivitiesImpl());\n\n    testEnv2.start();\n\n    // Replay should fail with worker that does not have interceptor registered\n    try {\n      WorkflowReplayer.replayWorkflowExecution(eventHistory, testEnv2Worker);\n      fail(\"This should have failed\");\n    } catch (Exception e) {\n      System.out.println(e.getMessage());\n    }\n\n    // But it should be fine with worker that does\n    try {\n      WorkflowReplayer.replayWorkflowExecution(eventHistory, worker);\n    } catch (Exception e) {\n      fail(e.getMessage());\n    }\n\n    testEnv2.shutdown();\n  }\n\n  // Test workflow and activities\n  @WorkflowInterface\n  public interface TestWorkflow {\n    @WorkflowMethod\n    void execute();\n  }\n\n  public static class TestWorkflowImpl implements TestWorkflow {\n\n    TestActivities activities =\n        Workflow.newActivityStub(\n            TestActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    @Override\n    public void execute() {\n      activities.activityOne();\n    }\n  }\n\n  @ActivityInterface\n  public interface TestActivities {\n    void activityOne();\n\n    void activityTwo();\n\n    void activityThree();\n  }\n\n  public static class TestActivitiesImpl implements TestActivities {\n    @Override\n    public void activityOne() {\n      System.out.println(\"Activities one done\");\n    }\n\n    @Override\n    public void activityTwo() {\n      System.out.println(\"Activities two done\");\n    }\n\n    @Override\n    public void activityThree() {\n      System.out.println(\"Activities three done\");\n    }\n  }\n\n  // Test worker and workflow interceptors\n  public static class TestWorkerInterceptor extends WorkerInterceptorBase {\n    @Override\n    public WorkflowInboundCallsInterceptor interceptWorkflow(WorkflowInboundCallsInterceptor next) {\n      return new TestWorkflowInboundCallsInterceptor(next);\n    }\n  }\n\n  public static class TestWorkflowInboundCallsInterceptor\n      extends WorkflowInboundCallsInterceptorBase {\n    TestActivities activities =\n        Workflow.newActivityStub(\n            TestActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    public TestWorkflowInboundCallsInterceptor(WorkflowInboundCallsInterceptor next) {\n      super(next);\n    }\n\n    @Override\n    public void init(WorkflowOutboundCallsInterceptor outboundCalls) {\n      super.init(new TestWorkflowOutboundCallsInterceptor(outboundCalls));\n    }\n\n    @Override\n    public WorkflowOutput execute(WorkflowInput input) {\n      WorkflowOutput output = super.execute(input);\n      // Run activity three before completing execution\n      activities.activityThree();\n      return output;\n    }\n  }\n\n  public static class TestWorkflowOutboundCallsInterceptor\n      extends WorkflowOutboundCallsInterceptorBase {\n    TestActivities activities =\n        Workflow.newActivityStub(\n            TestActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n    public TestWorkflowOutboundCallsInterceptor(WorkflowOutboundCallsInterceptor next) {\n      super(next);\n    }\n\n    @Override\n    public <R> ActivityOutput<R> executeActivity(ActivityInput<R> input) {\n      ActivityOutput output = super.executeActivity(input);\n\n      // we only want to intercept ActivityOne here\n      if (input.getActivityName().equals(\"ActivityOne\")) {\n        activities.activityTwo();\n      }\n\n      return output;\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/listworkflows/ListWorkflowsTest.java",
    "content": "package io.temporal.samples.listworkflows;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.api.workflow.v1.WorkflowExecutionInfo;\nimport io.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsRequest;\nimport io.temporal.api.workflowservice.v1.ListOpenWorkflowExecutionsResponse;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.List;\nimport java.util.Map;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class ListWorkflowsTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(CustomerWorkflowImpl.class)\n          .setActivityImplementations(new CustomerActivitiesImpl())\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    // create some fake customers\n    List<Customer> customers = new ArrayList<>();\n    customers.add(new Customer(\"c1\", \"John\", \"john@john.com\", \"new\"));\n    customers.add(new Customer(\"c2\", \"Mary\", \"mary@mary.com\", \"established\"));\n    customers.add(new Customer(\"c3\", \"Richard\", \"richard@richard.com\", \"established\"));\n    customers.add(new Customer(\"c4\", \"Anna\", \"anna@anna.com\", \"new\"));\n    customers.add(new Customer(\"c5\", \"Michael\", \"michael@michael.com\", \"established\"));\n\n    startWorkflows(customers);\n\n    List<WorkflowExecutionInfo> openExecutions = getExecutionsResponse();\n\n    assertEquals(5, openExecutions.size());\n\n    // signal exit to all customer workflows\n    stopWorkflows(customers);\n  }\n\n  private void startWorkflows(List<Customer> customers) {\n    // start a workflow for each customer that we need to add message to account\n    for (Customer c : customers) {\n      String message = \"New message for: \" + c.getName();\n      WorkflowOptions newCustomerWorkflowOptions =\n          WorkflowOptions.newBuilder()\n              .setWorkflowId(c.getAccountNum())\n              .setTaskQueue(testWorkflowRule.getTaskQueue())\n              // set the search attributes for this customer workflow\n              .setSearchAttributes(generateSearchAttributesFor(c))\n              .build();\n      CustomerWorkflow newCustomerWorkflow =\n          testWorkflowRule\n              .getWorkflowClient()\n              .newWorkflowStub(CustomerWorkflow.class, newCustomerWorkflowOptions);\n      // start async\n      WorkflowClient.start(newCustomerWorkflow::updateAccountMessage, c, message);\n    }\n  }\n\n  private Map<String, Object> generateSearchAttributesFor(Customer customer) {\n    Map<String, Object> searchAttributes = new HashMap<>();\n    searchAttributes.put(\"CustomStringField\", customer.getCustomerType());\n\n    return searchAttributes;\n  }\n\n  private List<WorkflowExecutionInfo> getExecutionsResponse() {\n    // List open workflows and validate their types\n    ListOpenWorkflowExecutionsRequest listRequest =\n        ListOpenWorkflowExecutionsRequest.newBuilder()\n            .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n            .build();\n    ListOpenWorkflowExecutionsResponse listResponse =\n        testWorkflowRule\n            .getTestEnvironment()\n            .getWorkflowService()\n            .blockingStub()\n            .listOpenWorkflowExecutions(listRequest);\n    return listResponse.getExecutionsList();\n  }\n\n  private void stopWorkflows(List<Customer> customers) {\n    for (Customer c : customers) {\n      CustomerWorkflow existingCustomerWorkflow =\n          testWorkflowRule\n              .getWorkflowClient()\n              .newWorkflowStub(CustomerWorkflow.class, c.getAccountNum());\n      // signal the exist method to stop execution\n      existingCustomerWorkflow.exit();\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/metrics/MetricsTest.java",
    "content": "package io.temporal.samples.metrics;\n\nimport static junit.framework.TestCase.assertEquals;\n\nimport com.google.common.collect.ImmutableMap;\nimport com.uber.m3.tally.RootScopeBuilder;\nimport com.uber.m3.tally.Scope;\nimport com.uber.m3.tally.StatsReporter;\nimport com.uber.m3.util.Duration;\nimport io.micrometer.core.instrument.Counter;\nimport io.micrometer.core.instrument.ImmutableTag;\nimport io.micrometer.core.instrument.Tag;\nimport io.micrometer.core.instrument.simple.SimpleMeterRegistry;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.reporter.MicrometerClientStatsReporter;\nimport io.temporal.samples.metrics.activities.MetricsActivitiesImpl;\nimport io.temporal.samples.metrics.workflow.MetricsWorkflow;\nimport io.temporal.samples.metrics.workflow.MetricsWorkflowImpl;\nimport io.temporal.serviceclient.MetricsTag;\nimport io.temporal.serviceclient.WorkflowServiceStubs;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkerOptions;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport org.jetbrains.annotations.NotNull;\nimport org.junit.After;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class MetricsTest {\n\n  private static final long REPORTING_FLUSH_TIME = 50;\n  private static List<Tag> TAGS_NAMESPACE_QUEUE;\n  private final String SDK_CUSTOM_KEY = \"sdkCustomTag1Key\";\n  private final String SDK_CUSTOM_VALUE = \"sdkCustomTag1Value\";\n  private final SimpleMeterRegistry registry = new SimpleMeterRegistry();\n  private final StatsReporter reporter = new MicrometerClientStatsReporter(registry);\n  private final Scope metricsScope =\n      new RootScopeBuilder()\n          .tags(ImmutableMap.of(SDK_CUSTOM_KEY, SDK_CUSTOM_VALUE))\n          .reporter(reporter)\n          .reportEvery(Duration.ofMillis(REPORTING_FLUSH_TIME >> 1));\n  private final String TEST_NAMESPACE = \"UnitTest\";\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(MetricsWorkflowImpl.class)\n          .setMetricsScope(metricsScope)\n          .setWorkerOptions(WorkerOptions.newBuilder().build())\n          .setActivityImplementations(new MetricsActivitiesImpl())\n          .build();\n\n  private WorkflowServiceStubs clientStubs;\n  private WorkflowClient workflowClient;\n\n  private static List<Tag> replaceTags(List<Tag> tags, String... nameValuePairs) {\n    for (int i = 0; i < nameValuePairs.length; i += 2) {\n      tags = replaceTag(tags, nameValuePairs[i], nameValuePairs[i + 1]);\n    }\n    return tags;\n  }\n\n  private static List<Tag> replaceTag(List<Tag> tags, String name, String value) {\n    List<Tag> result =\n        tags.stream().filter(tag -> !name.equals(tag.getKey())).collect(Collectors.toList());\n    result.add(new ImmutableTag(name, value));\n    return result;\n  }\n\n  @Before\n  public void setUp() {\n\n    final WorkflowServiceStubsOptions options =\n        testWorkflowRule.getWorkflowClient().getWorkflowServiceStubs().getOptions();\n\n    this.clientStubs = WorkflowServiceStubs.newServiceStubs(options);\n\n    this.workflowClient =\n        WorkflowClient.newInstance(clientStubs, testWorkflowRule.getWorkflowClient().getOptions());\n\n    final Map<String, String> stringStringMap = MetricsTag.defaultTags(TEST_NAMESPACE);\n    final List<Tag> TAGS_NAMESPACE =\n        stringStringMap.entrySet().stream()\n            .map(\n                nameValueEntry ->\n                    new ImmutableTag(nameValueEntry.getKey(), nameValueEntry.getValue()))\n            .collect(Collectors.toList());\n\n    TAGS_NAMESPACE_QUEUE =\n        replaceTags(TAGS_NAMESPACE, MetricsTag.TASK_QUEUE, testWorkflowRule.getTaskQueue());\n  }\n\n  @After\n  public void tearDown() {\n    this.clientStubs.shutdownNow();\n    this.registry.close();\n  }\n\n  @Test\n  public void testCountActivityRetriesMetric() throws InterruptedException {\n    final MetricsWorkflow metricsWorkflow =\n        workflowClient.newWorkflowStub(\n            MetricsWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(testWorkflowRule.getTaskQueue())\n                .validateBuildWithDefaults());\n\n    metricsWorkflow.exec(\"hello metrics\");\n\n    Thread.sleep(REPORTING_FLUSH_TIME);\n\n    assertIntCounter(4, countMetricActivityRetriesForActivity(\"PerformB\"));\n\n    assertIntCounter(2, countMetricActivityRetriesForActivity(\"PerformA\"));\n  }\n\n  @NotNull\n  private Counter countMetricActivityRetriesForActivity(String performB) {\n    final List<Tag> tags =\n        replaceTags(\n            TAGS_NAMESPACE_QUEUE,\n            MetricsTag.ACTIVITY_TYPE,\n            performB,\n            MetricsTag.WORKFLOW_TYPE,\n            \"MetricsWorkflow\",\n            MetricsTag.WORKER_TYPE,\n            \"ActivityWorker\",\n            SDK_CUSTOM_KEY,\n            SDK_CUSTOM_VALUE);\n    return registry.counter(\"custom_activity_retries\", tags);\n  }\n\n  private void assertIntCounter(int expectedValue, Counter counter) {\n    assertEquals(expectedValue, Math.round(counter.count()));\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/moneybatch/TransferWorkflowTest.java",
    "content": "package io.temporal.samples.moneybatch;\n\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.util.Random;\nimport java.util.UUID;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class TransferWorkflowTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(AccountTransferWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testTransfer() {\n    Account activities = mock(Account.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    String from = \"account1\";\n    String to = \"account2\";\n    int batchSize = 5;\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder()\n            .setTaskQueue(testWorkflowRule.getTaskQueue())\n            .setWorkflowId(to)\n            .build();\n    AccountTransferWorkflow transferWorkflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(AccountTransferWorkflow.class, options);\n    WorkflowClient.start(transferWorkflow::deposit, to, batchSize);\n    Random random = new Random();\n    int total = 0;\n    for (int i = 0; i < batchSize; i++) {\n      int amountCents = random.nextInt(1000);\n      transferWorkflow.withdraw(from, UUID.randomUUID().toString(), amountCents);\n      total += amountCents;\n    }\n    // Wait for workflow to finish\n    WorkflowStub.fromTyped(transferWorkflow).getResult(Void.class);\n    verify(activities).deposit(eq(\"account2\"), any(), eq(total));\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/moneytransfer/TransferWorkflowTest.java",
    "content": "package io.temporal.samples.moneytransfer;\n\nimport static org.mockito.ArgumentMatchers.eq;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class TransferWorkflowTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(AccountTransferWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testTransfer() {\n    Account activities = mock(Account.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n\n    testWorkflowRule.getTestEnvironment().start();\n\n    WorkflowOptions options =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    AccountTransferWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(AccountTransferWorkflow.class, options);\n\n    long start = testWorkflowRule.getTestEnvironment().currentTimeMillis();\n    workflow.transfer(\"account1\", \"account2\", \"reference1\", 123);\n    long duration = testWorkflowRule.getTestEnvironment().currentTimeMillis() - start;\n    System.out.println(\"Duration hours: \" + duration / 3600000);\n\n    verify(activities).withdraw(eq(\"account1\"), eq(\"reference1\"), eq(123));\n    verify(activities).deposit(eq(\"account2\"), eq(\"reference1\"), eq(123));\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5MockTest.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.samples.nexus.handler.EchoClient;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflow;\nimport io.temporal.samples.nexus.handler.SampleNexusServiceImpl;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n// This is an example of how to unit test Nexus services in JUnit5. The handlers are mocked,\n// so that the caller classes interact with the mocks and not the handler classes themselves.\n\n// @@@SNIPSTART java-nexus-sample-junit5-mock\npublic class CallerWorkflowJunit5MockTest {\n\n  // Sync Nexus operations run inline in the handler thread — there is no backing workflow to\n  // register a factory for. To mock one, inject a mock dependency into the service implementation.\n  private static final EchoClient mockEchoClient = mock(EchoClient.class);\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          // If a Nexus service is registered as part of the test as in the following line of code,\n          // the TestWorkflowExtension will, by default, automatically create a Nexus service\n          // endpoint and workflows registered as part of the TestWorkflowExtension will\n          // automatically inherit the endpoint if none is set.\n          .setNexusServiceImplementation(new SampleNexusServiceImpl(mockEchoClient))\n          // The Echo Nexus handler service just makes a call to a class, so no extra setup is\n          // needed. But the Hello Nexus service needs a worker for both the caller and handler\n          // in order to run, and the Echo Nexus caller service needs a worker.\n          //\n          // registerWorkflowImplementationTypes will take the classes given and create workers for\n          // them, enabling workflows to run.\n          .registerWorkflowImplementationTypes(\n              HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testHelloWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) {\n    // Workflows started by a Nexus service can be mocked just like any other workflow\n    worker.registerWorkflowImplementationFactory(\n        HelloHandlerWorkflow.class,\n        () -> {\n          HelloHandlerWorkflow mockHandler = mock(HelloHandlerWorkflow.class);\n          when(mockHandler.hello(any()))\n              .thenReturn(new SampleNexusService.HelloOutput(\"Hello Mock World 👋\"));\n          return mockHandler;\n        });\n    testEnv.start();\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.hello(\"World\", SampleNexusService.Language.EN);\n    assertEquals(\"Hello Mock World 👋\", greeting);\n\n    testEnv.shutdown();\n  }\n\n  @Test\n  public void testEchoWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) {\n    // Sync Nexus operations run inline in the handler thread — there is no backing workflow to\n    // register a factory for. Instead, stub the injected EchoClient dependency directly.\n    when(mockEchoClient.echo(any())).thenReturn(new SampleNexusService.EchoOutput(\"mocked echo\"));\n    testEnv.start();\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.echo(\"Hello\");\n    assertEquals(\"mocked echo\", greeting);\n\n    testEnv.shutdown();\n  }\n}\n\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowJunit5Test.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\n\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl;\nimport io.temporal.samples.nexus.handler.SampleNexusServiceImpl;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n// This is an example of how to unit test Nexus services in JUnit5. The handlers are not mocked,\n// but are actually called by the testing framework by the caller classes.\n\npublic class CallerWorkflowJunit5Test {\n\n  @RegisterExtension\n  public static final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          // If a Nexus service is registered as part of the test as in the following line of code,\n          // the TestWorkflowExtension will, by default, automatically create a Nexus service\n          // endpoint and workflows registered as part of the TestWorkflowExtension will\n          // automatically inherit the endpoint if none is set.\n          .setNexusServiceImplementation(new SampleNexusServiceImpl())\n          // The Echo Nexus handler service just makes a call to a class, so no extra setup is\n          // needed. But the Hello Nexus service needs a worker for both the caller and handler\n          // in order to run, and the Echo Nexus caller service needs a worker.\n          //\n          // registerWorkflowImplementationTypes will take the classes given and create workers for\n          // them, enabling workflows to run.\n          .registerWorkflowImplementationTypes(\n              HelloCallerWorkflowImpl.class,\n              HelloHandlerWorkflowImpl.class,\n              EchoCallerWorkflowImpl.class)\n          // The workflow will start before each test, and will shut down after each test.\n          // See CallerWorkflowTest for an example of how to control this differently if needed.\n          .build();\n\n  // The TestWorkflowExtension extension in the Temporal testing library creates the\n  // arguments to the test cases and initializes them from the extension setup call above.\n  @Test\n  public void testHelloWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) {\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.hello(\"World\", SampleNexusService.Language.EN);\n    assertEquals(\"Hello World 👋\", greeting);\n  }\n\n  @Test\n  public void testEchoWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) {\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.echo(\"Hello\");\n    assertEquals(\"Hello\", greeting);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowMockTest.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.when;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.handler.EchoClient;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflow;\nimport io.temporal.samples.nexus.handler.SampleNexusServiceImpl;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n// This is an example of how to unit test Nexus services in JUnit4. The handlers are mocked,\n// so that the caller classes interact with the mocks and not the handler classes themselves.\n\n// @@@SNIPSTART java-nexus-sample-junit4-mock\npublic class CallerWorkflowMockTest {\n\n  // Inject a mock EchoClient so sync Nexus operations can be stubbed per test.\n  // JUnit 4 creates a new test class instance per test method, so this mock is fresh each time.\n  private final EchoClient mockEchoClient = mock(EchoClient.class);\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          // If a Nexus service is registered as part of the test as in the following line of code,\n          // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint\n          // and workflows registered as part of the TestWorkflowRule\n          // will automatically inherit the endpoint if none is set.\n          .setNexusServiceImplementation(new SampleNexusServiceImpl(mockEchoClient))\n          // The Echo Nexus handler service just makes a call to a class, so no extra setup is\n          // needed. But the Hello Nexus service needs a worker for both the caller and handler\n          // in order to run.\n          // setWorkflowTypes will take the classes given and create workers for them, enabling\n          // workflows to run. This creates caller workflows, the handler workflows\n          // will be mocked in the test methods.\n          .setWorkflowTypes(HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class)\n          // Disable automatic worker startup as we are going to register some workflows manually\n          // per test\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testHelloWorkflow() {\n    testWorkflowRule\n        .getWorker()\n        // Workflows started by a Nexus service can be mocked just like any other workflow\n        .registerWorkflowImplementationFactory(\n            HelloHandlerWorkflow.class,\n            () -> {\n              HelloHandlerWorkflow wf = mock(HelloHandlerWorkflow.class);\n              when(wf.hello(any()))\n                  .thenReturn(new SampleNexusService.HelloOutput(\"Hello Mock World 👋\"));\n              return wf;\n            });\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Now create the caller workflow\n    HelloCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    String greeting = workflow.hello(\"World\", SampleNexusService.Language.EN);\n    assertEquals(\"Hello Mock World 👋\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testEchoWorkflow() {\n    // Sync Nexus operations run inline in the handler thread — there is no backing workflow to\n    // register a factory for. Instead, stub the injected EchoCient dependency directly.\n    when(mockEchoClient.echo(any())).thenReturn(new SampleNexusService.EchoOutput(\"mocked echo\"));\n    testWorkflowRule.getTestEnvironment().start();\n\n    EchoCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                EchoCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    String greeting = workflow.echo(\"Hello\");\n    assertEquals(\"mocked echo\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/CallerWorkflowTest.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.handler.HelloHandlerWorkflowImpl;\nimport io.temporal.samples.nexus.handler.SampleNexusServiceImpl;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport io.temporal.workflow.NexusServiceOptions;\nimport java.util.Collections;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n// This is an example of how to unit test Nexus services in JUnit4. The handlers are not mocked,\n// but are actually called by the testing framework by the caller classes.\n\npublic class CallerWorkflowTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          // If a Nexus service is registered as part of the test as in the following line of code,\n          // the TestWorkflowRule will, by default, automatically create a Nexus service endpoint\n          // and workflows registered as part of the TestWorkflowRule\n          // will automatically inherit the endpoint if none is set.\n          .setNexusServiceImplementation(new SampleNexusServiceImpl())\n          // The Echo Nexus handler service just makes a call to a class, so no extra setup is\n          // needed. But the Hello Nexus service needs a worker for both the caller and handler\n          // in order to run.\n          // setWorkflowTypes will take the classes given and create workers for them, enabling\n          // workflows to run. This is not adding an EchoCallerWorkflow though -\n          // see the testEchoWorkflow test method below for an example of an alternate way\n          // to supply a worker that gives you more flexibility if needed.\n          .setWorkflowTypes(HelloCallerWorkflowImpl.class, HelloHandlerWorkflowImpl.class)\n          // Disable automatic worker startup as we are going to register some workflows manually\n          // per test\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testHelloWorkflow() {\n    testWorkflowRule.getTestEnvironment().start();\n\n    HelloCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    String greeting = workflow.hello(\"World\", SampleNexusService.Language.EN);\n    assertEquals(\"Hello World 👋\", greeting);\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testEchoWorkflow() {\n    // If Workflows are registered later than the endpoint can be set manually\n    // either by setting the endpoint in the NexusServiceOptions in the Workflow implementation or\n    // by setting the NexusServiceOptions on the WorkflowImplementationOptions when registering\n    // the Workflow. To demonstrate, this is creating the Nexus service for Echo,\n    // and registering a EchoCallerWorkflowImpl worker.\n    //\n    // It is much simpler to use the setWorkflowTypes in the rule definition above - and as\n    // this isn't easily do-able in JUnit5 (the nexus endpoint isn't exposed) should be\n    // used with caution.\n    testWorkflowRule\n        .getWorker()\n        .registerWorkflowImplementationTypes(\n            WorkflowImplementationOptions.newBuilder()\n                .setNexusServiceOptions(\n                    Collections.singletonMap(\n                        \"SampleNexusService\",\n                        NexusServiceOptions.newBuilder()\n                            .setEndpoint(testWorkflowRule.getNexusEndpoint().getSpec().getName())\n                            .build()))\n                .build(),\n            EchoCallerWorkflowImpl.class);\n    testWorkflowRule.getTestEnvironment().start();\n\n    EchoCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                EchoCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    String greeting = workflow.echo(\"Hello\");\n    assertEquals(\"Hello\", greeting);\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceJunit5Test.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\n// This unit test example shows how to mock the Nexus service itself in JUnit4.\n// This is the path to take when you don't have access to the service implementation so\n// cannot mock it. Since the SampleNexusService itself is mocked,\n// no handlers need to be set up or mocked.\n\n// @@@SNIPSTART java-nexus-service-sample-junit5-mock\npublic class NexusServiceJunit5Test {\n\n  private final SampleNexusService mockNexusService = mock(SampleNexusService.class);\n\n  /**\n   * A test-only Nexus service implementation that delegates to the Mockito mock defined above. Both\n   * operations are implemented as synchronous handlers that forward calls to the mock, allowing\n   * full control over return values and verification of inputs.\n   */\n  @ServiceImpl(service = SampleNexusService.class)\n  public class TestNexusServiceImpl {\n    @OperationImpl\n    @SuppressWarnings(\"DirectInvocationOnMock\")\n    public OperationHandler<SampleNexusService.EchoInput, SampleNexusService.EchoOutput> echo() {\n      return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input));\n    }\n\n    @OperationImpl\n    @SuppressWarnings(\"DirectInvocationOnMock\")\n    public OperationHandler<SampleNexusService.HelloInput, SampleNexusService.HelloOutput> hello() {\n      return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input));\n    }\n  }\n\n  // Using OperationHandler.sync for both operations bypasses the need for a backing workflow,\n  // returning results inline just like a synchronous call.\n  //\n  // Note that the Mocks need to be done before the extension\n  // is defined, as creating the rule will fail if either call is still null.\n\n  @RegisterExtension\n  public final TestWorkflowExtension testWorkflowExtension =\n      TestWorkflowExtension.newBuilder()\n          // If a Nexus service is registered as part of the test as in the following line of code,\n          // the TestWorkflowExtension will, by default, automatically create a Nexus service\n          // endpoint and workflows registered as part of the TestWorkflowExtension will\n          // automatically inherit the endpoint if none is set.\n          .setNexusServiceImplementation(new TestNexusServiceImpl())\n          // The Echo Nexus handler service just makes a call to a class, so no extra setup is\n          // needed. But the Hello Nexus service needs a worker for both the caller and handler\n          // in order to run, and the Echo Nexus caller service needs a worker.\n          //\n          // registerWorkflowImplementationTypes will take the classes given and create workers for\n          // them, enabling workflows to run.\n          // Since both operations are mocked with OperationHandler.sync, no backing workflow is\n          // needed for hello — only the caller workflow types need to be registered.\n          .registerWorkflowImplementationTypes(\n              HelloCallerWorkflowImpl.class, EchoCallerWorkflowImpl.class)\n          // The workflow will start before each test, and will shut down after each test.\n          // See CallerWorkflowTest for an example of how to control this differently if needed.\n          .build();\n\n  // The TestWorkflowExtension extension in the Temporal testing library creates the\n  // arguments to the test cases and initializes them from the extension setup call above.\n  @Test\n  public void testHelloWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, HelloCallerWorkflow workflow) {\n\n    // Set the mock value to return\n    when(mockNexusService.hello(any()))\n        .thenReturn(new SampleNexusService.HelloOutput(\"Hello Mock World 👋\"));\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.hello(\"World\", SampleNexusService.Language.EN);\n    assertEquals(\"Hello Mock World 👋\", greeting);\n  }\n\n  @Test\n  public void testEchoWorkflow(\n      TestWorkflowEnvironment testEnv, Worker worker, EchoCallerWorkflow workflow) {\n    when(mockNexusService.echo(any()))\n        .thenReturn(new SampleNexusService.EchoOutput(\"echo response\"));\n\n    // Execute a workflow waiting for it to complete.\n    String greeting = workflow.echo(\"echo input\");\n    assertEquals(\"echo response\", greeting);\n\n    // Verify the echo operation was called exactly once and no other operations were invoked\n    verify(mockNexusService, times(1)).echo(any());\n    // Verify the Nexus service was called with the correct input\n    verify(mockNexusService).echo(argThat(input -> \"echo input\".equals(input.getMessage())));\n\n    verifyNoMoreInteractions(mockNexusService);\n  }\n}\n\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/nexus/caller/NexusServiceMockTest.java",
    "content": "package io.temporal.samples.nexus.caller;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.ArgumentMatchers.any;\nimport static org.mockito.Mockito.*;\n\nimport io.nexusrpc.handler.OperationHandler;\nimport io.nexusrpc.handler.OperationImpl;\nimport io.nexusrpc.handler.ServiceImpl;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.nexus.service.SampleNexusService;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n// This unit test example shows how to mock the Nexus service itself in JUnit4.\n// This is the path to take when you don't have access to the service implementation so\n// cannot mock it. Since the SampleNexusService itself is mocked,\n// no handlers need to be set up or mocked.\n\n// @@@SNIPSTART java-nexus-service-sample-junit4-mock\npublic class NexusServiceMockTest {\n\n  private final SampleNexusService mockNexusService = mock(SampleNexusService.class);\n\n  /**\n   * A test-only Nexus service implementation that delegates to the Mockito mock defined above. Both\n   * operations are implemented as synchronous handlers that forward calls to the mock, allowing\n   * full control over return values and verification of inputs.\n   */\n  @ServiceImpl(service = SampleNexusService.class)\n  public class TestNexusServiceImpl {\n    @OperationImpl\n    @SuppressWarnings(\"DirectInvocationOnMock\")\n    public OperationHandler<SampleNexusService.EchoInput, SampleNexusService.EchoOutput> echo() {\n      return OperationHandler.sync((ctx, details, input) -> mockNexusService.echo(input));\n    }\n\n    @OperationImpl\n    @SuppressWarnings(\"DirectInvocationOnMock\")\n    public OperationHandler<SampleNexusService.HelloInput, SampleNexusService.HelloOutput> hello() {\n      return OperationHandler.sync((ctx, details, input) -> mockNexusService.hello(input));\n    }\n  }\n\n  // Using OperationHandler.sync for both operations bypasses the need for a backing workflow,\n  // returning results inline just like a synchronous call.\n  //\n  // Note that the Mocks need to be done before the rule\n  // is defined, as creating the rule will fail if either call is still null.\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setNexusServiceImplementation(new TestNexusServiceImpl())\n          .setWorkflowTypes(EchoCallerWorkflowImpl.class, HelloCallerWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testHelloCallerWithMockedService() {\n    when(mockNexusService.hello(any()))\n        .thenReturn(new SampleNexusService.HelloOutput(\"Bonjour World\"));\n\n    HelloCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                HelloCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    String result = workflow.hello(\"World\", SampleNexusService.Language.FR);\n    assertEquals(\"Bonjour World\", result);\n\n    // Verify the Nexus service was called with the correct name and language\n    verify(mockNexusService)\n        .hello(\n            argThat(\n                input ->\n                    \"World\".equals(input.getName())\n                        && SampleNexusService.Language.FR == input.getLanguage()));\n  }\n\n  @Test\n  public void testEchoCallerWithMockedService() {\n    when(mockNexusService.echo(any()))\n        .thenReturn(new SampleNexusService.EchoOutput(\"echo response\"));\n\n    EchoCallerWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                EchoCallerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    String echoOutput = workflow.echo(\"echo input\");\n\n    assertEquals(\"echo response\", echoOutput);\n\n    // Verify the echo operation was called exactly once and no other operations were invoked\n    verify(mockNexusService, times(1)).echo(any());\n    // Verify the Nexus service was called with the correct input\n    verify(mockNexusService).echo(argThat(input -> \"echo input\".equals(input.getMessage())));\n\n    verifyNoMoreInteractions(mockNexusService);\n  }\n}\n\n// @@@SNIPEND\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/payloadconverter/CloudEventsPayloadConverterTest.java",
    "content": "package io.temporal.samples.payloadconverter;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.junit.Assert.assertNotNull;\n\nimport io.cloudevents.CloudEvent;\nimport io.cloudevents.core.builder.CloudEventBuilder;\nimport io.cloudevents.jackson.JsonCloudEventData;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.samples.payloadconverter.cloudevents.CEWorkflow;\nimport io.temporal.samples.payloadconverter.cloudevents.CEWorkflowImpl;\nimport io.temporal.samples.payloadconverter.cloudevents.CloudEventsPayloadConverter;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.net.URI;\nimport java.nio.charset.Charset;\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class CloudEventsPayloadConverterTest {\n\n  private DefaultDataConverter ddc =\n      DefaultDataConverter.newDefaultInstance()\n          .withPayloadConverterOverrides(new CloudEventsPayloadConverter());\n\n  private WorkflowClientOptions workflowClientOptions =\n      WorkflowClientOptions.newBuilder().setDataConverter(ddc).build();\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowClientOptions(workflowClientOptions)\n          .setWorkflowTypes(CEWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    List<CloudEvent> cloudEventList = new ArrayList<>();\n\n    for (int i = 0; i < 10; i++) {\n      cloudEventList.add(\n          CloudEventBuilder.v1()\n              .withId(String.valueOf(100 + i))\n              .withType(\"example.demo\")\n              .withSource(URI.create(\"http://temporal.io\"))\n              .withData(\n                  \"application/json\",\n                  (\"{\\n\" + \"\\\"greeting\\\": \\\"hello \" + i + \"\\\"\\n\" + \"}\")\n                      .getBytes(Charset.defaultCharset()))\n              .build());\n    }\n\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    CEWorkflow workflow =\n        testWorkflowRule.getWorkflowClient().newWorkflowStub(CEWorkflow.class, workflowOptions);\n    // start async\n    WorkflowClient.start(workflow::exec, cloudEventList.get(0));\n\n    for (int j = 1; j < 10; j++) {\n      workflow.addEvent(cloudEventList.get(j));\n    }\n\n    // Get the CE result and get its data (JSON)\n    String result =\n        ((JsonCloudEventData) workflow.getLastEvent().getData()).getNode().get(\"greeting\").asText();\n\n    assertNotNull(result);\n    assertEquals(\"hello 9\", result);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/payloadconverter/CryptoPayloadConverterTest.java",
    "content": "package io.temporal.samples.payloadconverter;\n\nimport static org.junit.Assert.*;\n\nimport com.codingrodent.jackson.crypto.CryptoModule;\nimport com.codingrodent.jackson.crypto.EncryptionService;\nimport com.codingrodent.jackson.crypto.PasswordCryptoContext;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.common.converter.DefaultDataConverter;\nimport io.temporal.common.converter.JacksonJsonPayloadConverter;\nimport io.temporal.samples.payloadconverter.crypto.CryptoWorkflow;\nimport io.temporal.samples.payloadconverter.crypto.CryptoWorkflowImpl;\nimport io.temporal.samples.payloadconverter.crypto.MyCustomer;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class CryptoPayloadConverterTest {\n  private static final String encryptDecryptPassword = \"encryptDecryptPassword\";\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowClientOptions(\n              WorkflowClientOptions.newBuilder()\n                  .setDataConverter(\n                      DefaultDataConverter.newDefaultInstance()\n                          .withPayloadConverterOverrides(getCryptoJacksonJsonPayloadConverter()))\n                  .build())\n          .setWorkflowTypes(CryptoWorkflowImpl.class)\n          .build();\n\n  @Test\n  public void testEncryptedWorkflowData() {\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    CryptoWorkflow workflow =\n        testWorkflowRule.getWorkflowClient().newWorkflowStub(CryptoWorkflow.class, workflowOptions);\n\n    MyCustomer customer = workflow.exec(new MyCustomer(\"John\", 22));\n    assertNotNull(customer);\n    assertTrue(customer.isApproved());\n  }\n\n  private JacksonJsonPayloadConverter getCryptoJacksonJsonPayloadConverter() {\n    ObjectMapper objectMapper = new ObjectMapper();\n    // Create the Crypto Context (password based)\n    PasswordCryptoContext cryptoContext =\n        new PasswordCryptoContext(\n            encryptDecryptPassword, // decrypt password\n            encryptDecryptPassword, // encrypt password\n            PasswordCryptoContext.CIPHER_NAME, // cipher name\n            PasswordCryptoContext.KEY_NAME); // key generator names\n    EncryptionService encryptionService = new EncryptionService(objectMapper, cryptoContext);\n    objectMapper.registerModule(new CryptoModule().addEncryptionService(encryptionService));\n\n    return new JacksonJsonPayloadConverter(objectMapper);\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/peractivityoptions/PerActivityOptionsTest.java",
    "content": "package io.temporal.samples.peractivityoptions;\n\nimport static org.junit.Assert.*;\n\nimport com.google.common.collect.ImmutableMap;\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.api.enums.v1.WorkflowExecutionStatus;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionRequest;\nimport io.temporal.api.workflowservice.v1.DescribeWorkflowExecutionResponse;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class PerActivityOptionsTest {\n  WorkflowImplementationOptions options =\n      WorkflowImplementationOptions.newBuilder()\n          .setActivityOptions(\n              ImmutableMap.of(\n                  \"ActivityTypeA\",\n                  ActivityOptions.newBuilder()\n                      .setScheduleToCloseTimeout(Duration.ofSeconds(5))\n                      .build(),\n                  \"ActivityTypeB\",\n                  ActivityOptions.newBuilder()\n                      .setStartToCloseTimeout(Duration.ofSeconds(2))\n                      .setRetryOptions(\n                          RetryOptions.newBuilder()\n                              .setDoNotRetry(NullPointerException.class.getName())\n                              .build())\n                      .build()))\n          .build();\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(options, PerActivityOptionsWorkflowImpl.class)\n          .setActivityImplementations(new FailingActivitiesImpl())\n          .build();\n\n  @Test\n  public void testPerActivityTypeWorkflow() {\n    PerActivityOptionsWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                PerActivityOptionsWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    WorkflowExecution execution = untyped.start();\n    // wait until workflow completes\n    untyped.getResult(Void.class);\n\n    DescribeWorkflowExecutionResponse resp =\n        testWorkflowRule\n            .getWorkflowClient()\n            .getWorkflowServiceStubs()\n            .blockingStub()\n            .describeWorkflowExecution(\n                DescribeWorkflowExecutionRequest.newBuilder()\n                    .setNamespace(testWorkflowRule.getTestEnvironment().getNamespace())\n                    .setExecution(execution)\n                    .build());\n\n    assertEquals(\n        WorkflowExecutionStatus.WORKFLOW_EXECUTION_STATUS_COMPLETED,\n        resp.getWorkflowExecutionInfo().getStatus());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/polling/FrequentPollingTest.java",
    "content": "package io.temporal.samples.polling;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.polling.frequent.FrequentPollingActivityImpl;\nimport io.temporal.samples.polling.frequent.FrequentPollingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class FrequentPollingTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(FrequentPollingWorkflowImpl.class)\n          .setActivityImplementations(new FrequentPollingActivityImpl(new TestService()))\n          .build();\n\n  @Test\n  public void testInfrequentPoll() {\n    PollingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                PollingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"OK\", workflow.exec());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/polling/InfrequentPollingTest.java",
    "content": "package io.temporal.samples.polling;\n\nimport static org.junit.Assert.*;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.polling.infrequent.InfrequentPollingActivityImpl;\nimport io.temporal.samples.polling.infrequent.InfrequentPollingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class InfrequentPollingTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(InfrequentPollingWorkflowImpl.class)\n          .setActivityImplementations(new InfrequentPollingActivityImpl(new TestService()))\n          .build();\n\n  @Test\n  public void testInfrequentPoll() {\n    PollingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                PollingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"OK\", workflow.exec());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/polling/PeriodicPollingTest.java",
    "content": "package io.temporal.samples.polling;\n\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.polling.periodicsequence.PeriodicPollingActivityImpl;\nimport io.temporal.samples.polling.periodicsequence.PeriodicPollingChildWorkflowImpl;\nimport io.temporal.samples.polling.periodicsequence.PeriodicPollingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class PeriodicPollingTest {\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(\n              PeriodicPollingWorkflowImpl.class, PeriodicPollingChildWorkflowImpl.class)\n          .setActivityImplementations(new PeriodicPollingActivityImpl(new TestService()))\n          .build();\n\n  @Test\n  public void testInfrequentPoll() {\n    PollingWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                PollingWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"OK\", workflow.exec());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/retryonsignalinterceptor/RetryOnSignalInterceptorTest.java",
    "content": "package io.temporal.samples.retryonsignalinterceptor;\n\nimport static org.junit.Assert.*;\n\nimport io.temporal.api.common.v1.WorkflowExecution;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.time.Duration;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class RetryOnSignalInterceptorTest {\n\n  static class TestActivityImpl implements MyActivity {\n\n    final AtomicInteger count = new AtomicInteger();\n\n    @Override\n    public void execute() {\n      if (count.incrementAndGet() < 5) {\n        throw ApplicationFailure.newFailure(\"simulated\", \"type1\");\n      }\n    }\n  }\n\n  private final TestActivityImpl testActivity = new TestActivityImpl();\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkerFactoryOptions(\n              WorkerFactoryOptions.newBuilder()\n                  .setWorkerInterceptors(new RetryOnSignalWorkerInterceptor())\n                  .validateAndBuildWithDefaults())\n          .setWorkflowTypes(MyWorkflowImpl.class)\n          .setActivityImplementations(testActivity)\n          .build();\n\n  @Test\n  public void testRetryThenFail() {\n    testActivity.count.set(0);\n    TestWorkflowEnvironment testEnvironment = testWorkflowRule.getTestEnvironment();\n    MyWorkflow workflow = testWorkflowRule.newWorkflowStub(MyWorkflow.class);\n    WorkflowExecution execution = WorkflowClient.start(workflow::execute);\n\n    // Get stub to the dynamically registered interface\n    RetryOnSignalInterceptorListener listener =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(RetryOnSignalInterceptorListener.class, execution.getWorkflowId());\n    testEnvironment.sleep(Duration.ofMinutes(10));\n    listener.retry();\n    testEnvironment.sleep(Duration.ofMinutes(10));\n    listener.retry();\n    testEnvironment.sleep(Duration.ofMinutes(10));\n    listener.retry();\n    testEnvironment.sleep(Duration.ofMinutes(10));\n    listener.fail();\n    WorkflowStub untyped =\n        testWorkflowRule.getWorkflowClient().newUntypedWorkflowStub(execution.getWorkflowId());\n    try {\n      untyped.getResult(Void.class);\n      fail(\"unreachable\");\n    } catch (Exception e) {\n      assertTrue(e.getCause() instanceof ActivityFailure);\n      assertTrue(e.getCause().getCause() instanceof ApplicationFailure);\n      assertEquals(\n          \"message='simulated', type='type1', nonRetryable=false\",\n          e.getCause().getCause().getMessage());\n    }\n    assertEquals(4, testActivity.count.get());\n  }\n\n  @Test\n  public void testRetryUntilSucceeds() {\n    testActivity.count.set(0);\n    TestWorkflowEnvironment testEnvironment = testWorkflowRule.getTestEnvironment();\n    MyWorkflow workflow = testWorkflowRule.newWorkflowStub(MyWorkflow.class);\n    WorkflowExecution execution = WorkflowClient.start(workflow::execute);\n\n    // Get stub to the dynamically registered interface\n    RetryOnSignalInterceptorListener listener =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(RetryOnSignalInterceptorListener.class, execution.getWorkflowId());\n    for (int i = 0; i < 4; i++) {\n      testEnvironment.sleep(Duration.ofMinutes(10));\n      listener.retry();\n    }\n    WorkflowStub untyped =\n        testWorkflowRule.getWorkflowClient().newUntypedWorkflowStub(execution.getWorkflowId());\n    untyped.getResult(Void.class);\n    assertEquals(5, testActivity.count.get());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/safemessagepassing/ClusterManagerWorkflowWorkerTest.java",
    "content": "package io.temporal.samples.safemessagepassing;\n\nimport io.temporal.client.*;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport java.util.ArrayList;\nimport java.util.List;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ExecutionException;\nimport org.junit.Assert;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class ClusterManagerWorkflowWorkerTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(ClusterManagerWorkflowImpl.class)\n          .setActivityImplementations(new ClusterManagerActivitiesImpl())\n          .build();\n\n  @Test\n  public void testSafeMessageHandler() throws ExecutionException, InterruptedException {\n    ClusterManagerWorkflow cluster =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                ClusterManagerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    CompletableFuture<ClusterManagerWorkflow.ClusterManagerResult> result =\n        WorkflowClient.execute(\n            cluster::run, new ClusterManagerWorkflow.ClusterManagerInput(Optional.empty(), false));\n\n    cluster.startCluster();\n\n    List<CompletableFuture<ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult>>\n        assignJobs = new ArrayList<>();\n    for (int i = 0; i < 6; i++) {\n      assignJobs.add(\n          WorkflowStub.fromTyped(cluster)\n              .startUpdate(\n                  \"assignNodesToJobs\",\n                  WorkflowUpdateStage.ACCEPTED,\n                  ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult.class,\n                  new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(2, \"job-\" + i))\n              .getResultAsync());\n    }\n    assignJobs.forEach(\n        (f) -> {\n          try {\n            Assert.assertEquals(2, f.get().getNodesAssigned().size());\n          } catch (InterruptedException e) {\n            throw new RuntimeException(e);\n          } catch (ExecutionException e) {\n            throw new RuntimeException(e);\n          }\n        });\n\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofSeconds(1));\n\n    List<CompletableFuture<Void>> deleteJobs = new ArrayList<>();\n    for (int i = 0; i < 6; i++) {\n      deleteJobs.add(\n          WorkflowStub.fromTyped(cluster)\n              .startUpdate(\n                  \"deleteJob\",\n                  WorkflowUpdateStage.ACCEPTED,\n                  Void.class,\n                  new ClusterManagerWorkflow.ClusterManagerDeleteJobInput(\"job-\" + i))\n              .getResultAsync());\n    }\n    deleteJobs.forEach(CompletableFuture::join);\n\n    cluster.stopCluster();\n    Assert.assertEquals(0, result.get().getNumCurrentlyAssignedNodes());\n  }\n\n  @Test\n  public void testUpdateIdempotency() {\n    ClusterManagerWorkflow cluster =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                ClusterManagerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    WorkflowClient.execute(\n        cluster::run, new ClusterManagerWorkflow.ClusterManagerInput(Optional.empty(), false));\n\n    cluster.startCluster();\n\n    ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult result1 =\n        cluster.assignNodesToJobs(\n            new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(5, \"test-job\"));\n\n    ClusterManagerWorkflow.ClusterManagerAssignNodesToJobResult result2 =\n        cluster.assignNodesToJobs(\n            new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(5, \"test-job\"));\n\n    Assert.assertTrue(result1.getNodesAssigned().size() >= result2.getNodesAssigned().size());\n  }\n\n  @Test\n  public void testUpdateFailure() throws ExecutionException, InterruptedException {\n    ClusterManagerWorkflow cluster =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                ClusterManagerWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n    CompletableFuture<ClusterManagerWorkflow.ClusterManagerResult> result =\n        WorkflowClient.execute(\n            cluster::run, new ClusterManagerWorkflow.ClusterManagerInput(Optional.empty(), false));\n\n    cluster.startCluster();\n\n    cluster.assignNodesToJobs(\n        new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(24, \"big-job\"));\n    WorkflowUpdateException updateFailure =\n        Assert.assertThrows(\n            WorkflowUpdateException.class,\n            () ->\n                cluster.assignNodesToJobs(\n                    new ClusterManagerWorkflow.ClusterManagerAssignNodesToJobInput(\n                        3, \"little-job\")));\n    Assert.assertTrue(updateFailure.getCause() instanceof ApplicationFailure);\n    Assert.assertEquals(\n        \"Cannot assign nodes to a job: Not enough nodes available\",\n        ((ApplicationFailure) updateFailure.getCause()).getOriginalMessage());\n\n    cluster.stopCluster();\n    Assert.assertEquals(\n        24, result.get().getNumCurrentlyAssignedNodes() + result.get().getNumBadNodes());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysJUnit5Test.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.Mockito.*;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport io.temporal.testing.TestWorkflowExtension;\nimport io.temporal.worker.Worker;\nimport java.time.Duration;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.Timeout;\nimport org.junit.jupiter.api.extension.RegisterExtension;\n\npublic class SleepForDaysJUnit5Test {\n\n  @RegisterExtension\n  public TestWorkflowExtension testWorkflowRule =\n      TestWorkflowExtension.newBuilder()\n          .registerWorkflowImplementationTypes(SleepForDaysImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  @Timeout(8)\n  public void testSleepForDays(\n      TestWorkflowEnvironment testEnv, Worker worker, SleepForDaysWorkflow workflow) {\n    // Mock activity\n    SendEmailActivity activities = mock(SendEmailActivity.class);\n    worker.registerActivitiesImplementations(activities);\n    // Start environment\n    testEnv.start();\n\n    // Start workflow\n    WorkflowClient.start(workflow::sleepForDays);\n\n    long startTime = testEnv.currentTimeMillis();\n    // Time-skip 5 minutes.\n    testEnv.sleep(Duration.ofMinutes(5));\n    // Check that the activity has been called, we're now waiting for the sleep to finish.\n    verify(activities, times(1)).sendEmail(anyString());\n    // Time-skip 3 days.\n    testEnv.sleep(Duration.ofDays(90));\n    // Expect 3 more activity calls.\n    verify(activities, times(4)).sendEmail(anyString());\n    // Send the signal to complete the workflow.\n    workflow.complete();\n    // Expect no more activity calls to have been made - workflow is complete.\n    workflow.sleepForDays();\n    verify(activities, times(4)).sendEmail(anyString());\n    // Expect more than 90 days to have passed.\n    long endTime = testEnv.currentTimeMillis();\n    assertEquals(true, endTime - startTime > Duration.ofDays(90).toMillis());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/sleepfordays/SleepForDaysTest.java",
    "content": "package io.temporal.samples.sleepfordays;\n\nimport static org.junit.jupiter.api.Assertions.assertEquals;\nimport static org.mockito.ArgumentMatchers.anyString;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.times;\nimport static org.mockito.Mockito.verify;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class SleepForDaysTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(SleepForDaysImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test(timeout = 8000)\n  public void testSleepForDays() {\n    // Mock activity\n    SendEmailActivity activities = mock(SendEmailActivity.class);\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    // Start environment\n    testWorkflowRule.getTestEnvironment().start();\n\n    // Create a workflow\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    SleepForDaysWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(SleepForDaysWorkflow.class, workflowOptions);\n\n    // Start workflow\n    WorkflowClient.start(workflow::sleepForDays);\n\n    long startTime = testWorkflowRule.getTestEnvironment().currentTimeMillis();\n    // Time-skip 5 minutes.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofMinutes(5));\n    // Check that the activity has been called, we're now waiting for the sleep to finish.\n    verify(activities, times(1)).sendEmail(anyString());\n    // Time-skip 3 days.\n    testWorkflowRule.getTestEnvironment().sleep(Duration.ofDays(90));\n    // Expect 3 more activity calls.\n    verify(activities, times(4)).sendEmail(anyString());\n    // Send the signal to complete the workflow.\n    workflow.complete();\n    // Expect no more activity calls to have been made - workflow is complete.\n    verify(activities, times(4)).sendEmail(anyString());\n    // Expect more than 90 days to have passed.\n    long endTime = testWorkflowRule.getTestEnvironment().currentTimeMillis();\n    assertEquals(true, endTime - startTime > Duration.ofDays(90).toMillis());\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/standaloneactivities/StandaloneActivitiesTest.java",
    "content": "package io.temporal.samples.standaloneactivities;\n\nimport static org.junit.Assert.assertEquals;\nimport static org.mockito.Mockito.mock;\nimport static org.mockito.Mockito.verify;\nimport static org.mockito.Mockito.when;\nimport static org.mockito.Mockito.withSettings;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.time.Duration;\nimport org.junit.Rule;\nimport org.junit.Test;\n\n/**\n * Unit tests for the standaloneactivities sample. Uses an embedded Temporal test server so no\n * external service is required.\n *\n * <p>Standalone Activities do not use a Workflow at runtime, but the embedded test server only\n * supports Activity execution through a Workflow. These tests therefore drive the Activity through\n * a minimal wrapper Workflow so the Activity logic is exercised against the real SDK worker stack.\n */\npublic class StandaloneActivitiesTest {\n\n  @WorkflowInterface\n  public interface TestWorkflow {\n\n    @WorkflowMethod\n    String run(String greeting, String name);\n  }\n\n  public static class TestWorkflowImpl implements TestWorkflow {\n\n    private final GreetingActivities activities =\n        Workflow.newActivityStub(\n            GreetingActivities.class,\n            ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(5)).build());\n\n    @Override\n    public String run(String greeting, String name) {\n      return activities.composeGreeting(greeting, name);\n    }\n  }\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowTypes(TestWorkflowImpl.class)\n          .setDoNotStart(true)\n          .build();\n\n  @Test\n  public void testActivityImpl() {\n    testWorkflowRule.getWorker().registerActivitiesImplementations(new GreetingActivitiesImpl());\n    testWorkflowRule.getTestEnvironment().start();\n\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"Hello, World!\", workflow.run(\"Hello\", \"World\"));\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n\n  @Test\n  public void testMockedActivity() {\n    // withoutAnnotations() prevents Mockito from copying @ActivityMethod from the interface onto\n    // the mock, which would cause worker registration to fail.\n    GreetingActivities activities =\n        mock(GreetingActivities.class, withSettings().withoutAnnotations());\n    when(activities.composeGreeting(\"Hello\", \"World\")).thenReturn(\"Hello, World!\");\n    testWorkflowRule.getWorker().registerActivitiesImplementations(activities);\n    testWorkflowRule.getTestEnvironment().start();\n\n    TestWorkflow workflow =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newWorkflowStub(\n                TestWorkflow.class,\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    assertEquals(\"Hello, World!\", workflow.run(\"Hello\", \"World\"));\n    verify(activities).composeGreeting(\"Hello\", \"World\");\n\n    testWorkflowRule.getTestEnvironment().shutdown();\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/terminateworkflow/TerminateWorkflowTest.java",
    "content": "package io.temporal.samples.terminateworkflow;\n\nimport static org.junit.Assert.*;\nimport static org.junit.Assert.assertEquals;\n\nimport io.temporal.client.WorkflowFailedException;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.failure.TerminatedFailure;\nimport io.temporal.testing.TestWorkflowRule;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class TerminateWorkflowTest {\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder().setWorkflowTypes(MyWorkflowImpl.class).build();\n\n  @Test\n  public void testTerminateWorkflow() {\n    WorkflowStub wfs =\n        testWorkflowRule\n            .getWorkflowClient()\n            .newUntypedWorkflowStub(\n                \"MyWorkflow\",\n                WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build());\n\n    wfs.start(testWorkflowRule.getTaskQueue());\n    try {\n      Thread.sleep(1000);\n    } catch (Exception e) {\n      fail(e.getMessage());\n    }\n    wfs.terminate(\"Test Reasons\");\n    try {\n      wfs.getResult(String.class);\n      fail(\"unreachable\");\n    } catch (WorkflowFailedException ignored) {\n      assertTrue(ignored.getCause() instanceof TerminatedFailure);\n      assertEquals(\"Test Reasons\", ((TerminatedFailure) ignored.getCause()).getOriginalMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "core/src/test/java/io/temporal/samples/tracing/TracingTest.java",
    "content": "package io.temporal.samples.tracing;\n\nimport static org.junit.Assert.*;\n\nimport io.jaegertracing.internal.JaegerSpan;\nimport io.jaegertracing.internal.JaegerTracer;\nimport io.jaegertracing.internal.reporters.InMemoryReporter;\nimport io.jaegertracing.internal.samplers.ConstSampler;\nimport io.jaegertracing.spi.Sampler;\nimport io.opentracing.Tracer;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.opentracing.OpenTracingClientInterceptor;\nimport io.temporal.opentracing.OpenTracingOptions;\nimport io.temporal.opentracing.OpenTracingSpanContextCodec;\nimport io.temporal.opentracing.OpenTracingWorkerInterceptor;\nimport io.temporal.samples.tracing.workflow.TracingActivitiesImpl;\nimport io.temporal.samples.tracing.workflow.TracingChildWorkflowImpl;\nimport io.temporal.samples.tracing.workflow.TracingWorkflow;\nimport io.temporal.samples.tracing.workflow.TracingWorkflowImpl;\nimport io.temporal.testing.TestWorkflowRule;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport java.util.List;\nimport org.junit.After;\nimport org.junit.Rule;\nimport org.junit.Test;\n\npublic class TracingTest {\n  private final InMemoryReporter reporter = new InMemoryReporter();\n  private final Sampler sampler = new ConstSampler(true);\n  private final Tracer tracer =\n      new JaegerTracer.Builder(\"temporal-test\").withReporter(reporter).withSampler(sampler).build();\n\n  private final OpenTracingOptions JAEGER_COMPATIBLE_CONFIG =\n      OpenTracingOptions.newBuilder()\n          .setSpanContextCodec(OpenTracingSpanContextCodec.TEXT_MAP_CODEC)\n          .setTracer(tracer)\n          .build();\n\n  @Rule\n  public TestWorkflowRule testWorkflowRule =\n      TestWorkflowRule.newBuilder()\n          .setWorkflowClientOptions(\n              WorkflowClientOptions.newBuilder()\n                  .setInterceptors(new OpenTracingClientInterceptor(JAEGER_COMPATIBLE_CONFIG))\n                  .validateAndBuildWithDefaults())\n          .setWorkerFactoryOptions(\n              WorkerFactoryOptions.newBuilder()\n                  .setWorkerInterceptors(new OpenTracingWorkerInterceptor(JAEGER_COMPATIBLE_CONFIG))\n                  .validateAndBuildWithDefaults())\n          .setWorkflowTypes(TracingWorkflowImpl.class, TracingChildWorkflowImpl.class)\n          .setActivityImplementations(new TracingActivitiesImpl())\n          .build();\n\n  @After\n  public void tearDown() {\n    reporter.close();\n    sampler.close();\n    tracer.close();\n  }\n\n  @Test\n  public void testReportSpans() {\n    WorkflowClient client = testWorkflowRule.getWorkflowClient();\n    WorkflowOptions workflowOptions =\n        WorkflowOptions.newBuilder().setTaskQueue(testWorkflowRule.getTaskQueue()).build();\n    TracingWorkflow workflow = client.newWorkflowStub(TracingWorkflow.class, workflowOptions);\n\n    // Convert to untyped and start it with signalWithStart\n    WorkflowStub untyped = WorkflowStub.fromTyped(workflow);\n    untyped.signalWithStart(\"setLanguage\", new Object[] {\"Spanish\"}, new Object[] {\"John\"});\n\n    String greeting = untyped.getResult(String.class);\n    assertEquals(\"Hola John\", greeting);\n\n    List<JaegerSpan> reportedSpans = reporter.getSpans();\n    assertNotNull(reportedSpans);\n    assertEquals(7, reportedSpans.size());\n  }\n}\n"
  },
  {
    "path": "core/src/test/resources/dsl/sampleflow.json",
    "content": "{\n  \"id\": \"sampleFlow\",\n  \"name\": \"Sample Flow One\",\n  \"description\": \"Sample Flow Definition\",\n  \"actions\": [\n    {\n      \"action\": \"One\",\n      \"retries\": 10,\n      \"startToCloseSec\": 3\n    },\n    {\n      \"action\": \"Two\",\n      \"retries\": 8,\n      \"startToCloseSec\": 3\n    },\n    {\n      \"action\": \"Three\",\n      \"retries\": 10,\n      \"startToCloseSec\": 4\n    },\n    {\n      \"action\": \"Four\",\n      \"retries\": 9,\n      \"startToCloseSec\": 5\n    }\n  ]\n}"
  },
  {
    "path": "docker/github/Dockerfile",
    "content": "FROM eclipse-temurin:17-jammy\n\n# Git is needed in order to update the dls submodule\nRUN apt-get update && apt-get install -y wget protobuf-compiler git\n\nRUN mkdir /temporal-java-samples\nWORKDIR /temporal-java-samples\n"
  },
  {
    "path": "docker/github/README.md",
    "content": "# Using Github Actions\n\nGithub action simply runs Docker containers. So it is easy to perform the \nsame build locally that Github will do. To handle this, there are \ntwo different docker-compose files: one for Github and one for local.\nThe Dockerfile is the same for both. \n\n## Testing the build locally\nTo run the build locally, start from the root folder of this repo and run the following command:\n```bash\ndocker-compose -f docker/github/docker-compose.yaml run unit-test\n```\n\nNote that Github action will run basically the same commands.\n\n## Testing the build in Github Actions\nCreating a PR against the main branch will trigger the Github action.\n"
  },
  {
    "path": "docker/github/docker-compose.yaml",
    "content": "version: '3.5'\n\nservices:\n  unit-test:\n    build:\n      context: ../../\n      dockerfile: ./docker/github/Dockerfile\n    command: \"./gradlew --no-daemon test\"\n    environment:\n      - \"USER=unittest\"\n    volumes:\n      - \"../../:/temporal-java-samples\"\n"
  },
  {
    "path": "gradle/springai.gradle",
    "content": "// Shared configuration for all Spring AI sample modules.\n// Applied via: apply from: \"$rootDir/gradle/springai.gradle\"\n//\n// Note on Spring Boot version skew: the root build.gradle pins the\n// org.springframework.boot Gradle plugin at $springBootPluginVersion (currently\n// 2.7.13) for the legacy springboot/ samples. Spring AI 1.1.0 requires Spring\n// Boot 3.5.x, which we get by importing the spring-boot-dependencies BOM at\n// $springBootVersionForSpringAi below. The plugin and the BOM are independent —\n// the plugin contributes bootJar/bootRun task wiring, the BOM dictates\n// dependency versions — so this works in practice even though the two version\n// numbers don't match. Long-term fix is to either move the plugin declaration\n// out of the root plugins block (so each module applies its own version) or\n// migrate the legacy springboot/ samples to Spring Boot 3.x; until one of those\n// happens, this skew is intentional.\n\napply plugin: 'org.springframework.boot'\napply plugin: 'io.spring.dependency-management'\n\next {\n    springBootVersionForSpringAi = '3.5.3'\n    springAiVersion = '1.1.0'\n}\n\njava {\n    sourceCompatibility = JavaVersion.VERSION_17\n    targetCompatibility = JavaVersion.VERSION_17\n}\n\ndependencyManagement {\n    imports {\n        mavenBom \"org.springframework.boot:spring-boot-dependencies:$springBootVersionForSpringAi\"\n        mavenBom \"org.springframework.ai:spring-ai-bom:$springAiVersion\"\n    }\n}\n\ndependencies {\n    implementation \"io.temporal:temporal-spring-boot-starter:$javaSDKVersion\"\n    implementation \"io.temporal:temporal-spring-ai:$javaSDKVersion\"\n    // temporal-spring-ai declares temporal-sdk as compileOnly, so bring it in explicitly.\n    implementation \"io.temporal:temporal-sdk:$javaSDKVersion\"\n\n    // Spring Boot\n    implementation 'org.springframework.boot:spring-boot-starter'\n\n    dependencies {\n        errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')\n        errorprone('com.google.errorprone:error_prone_core:2.28.0')\n    }\n}\n\nbootJar {\n    enabled = false\n}\n\njar {\n    enabled = true\n}\n\nbootRun {\n    standardInput = System.in\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.10.2-bin.zip\nnetworkTimeout=10000\nvalidateDistributionUrl=true\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "springBootPluginVersion=2.7.13\n\n# use this for spring boot 3 support\n#springBootPluginVersion=3.2.0\n"
  },
  {
    "path": "gradlew",
    "content": "#!/bin/sh\n\n#\n# Copyright © 2015-2021 the original authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n# SPDX-License-Identifier: Apache-2.0\n#\n\n##############################################################################\n#\n#   Gradle start up script for POSIX generated by Gradle.\n#\n#   Important for running:\n#\n#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is\n#       noncompliant, but you have some other compliant shell such as ksh or\n#       bash, then to run this script, type that shell name before the whole\n#       command line, like:\n#\n#           ksh Gradle\n#\n#       Busybox and similar reduced shells will NOT work, because this script\n#       requires all of these POSIX shell features:\n#         * functions;\n#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,\n#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;\n#         * compound commands having a testable exit status, especially «case»;\n#         * various built-in commands including «command», «set», and «ulimit».\n#\n#   Important for patching:\n#\n#   (2) This script targets any POSIX shell, so it avoids extensions provided\n#       by Bash, Ksh, etc; in particular arrays are avoided.\n#\n#       The \"traditional\" practice of packing multiple parameters into a\n#       space-separated string is a well documented source of bugs and security\n#       problems, so this is (mostly) avoided, by progressively accumulating\n#       options in \"$@\", and eventually passing that to Java.\n#\n#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,\n#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;\n#       see the in-line comments for details.\n#\n#       There are tweaks for specific operating systems such as AIX, CygWin,\n#       Darwin, MinGW, and NonStop.\n#\n#   (3) This script is generated from the Groovy template\n#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt\n#       within the Gradle project.\n#\n#       You can find Gradle at https://github.com/gradle/gradle/.\n#\n##############################################################################\n\n# Attempt to set APP_HOME\n\n# Resolve links: $0 may be a link\napp_path=$0\n\n# Need this for daisy-chained symlinks.\nwhile\n    APP_HOME=${app_path%\"${app_path##*/}\"}  # leaves a trailing /; empty if no leading path\n    [ -h \"$app_path\" ]\ndo\n    ls=$( ls -ld \"$app_path\" )\n    link=${ls#*' -> '}\n    case $link in             #(\n      /*)   app_path=$link ;; #(\n      *)    app_path=$APP_HOME$link ;;\n    esac\ndone\n\n# This is normally unused\n# shellcheck disable=SC2034\nAPP_BASE_NAME=${0##*/}\n# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)\nAPP_HOME=$( cd -P \"${APP_HOME:-./}\" > /dev/null && printf '%s\n' \"$PWD\" ) || exit\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=maximum\n\nwarn () {\n    echo \"$*\"\n} >&2\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n} >&2\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"$( uname )\" in                #(\n  CYGWIN* )         cygwin=true  ;; #(\n  Darwin* )         darwin=true  ;; #(\n  MSYS* | MINGW* )  msys=true    ;; #(\n  NONSTOP* )        nonstop=true ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=$JAVA_HOME/jre/sh/java\n    else\n        JAVACMD=$JAVA_HOME/bin/java\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=java\n    if ! command -v java >/dev/null 2>&1\n    then\n        die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nfi\n\n# Increase the maximum file descriptors if we can.\nif ! \"$cygwin\" && ! \"$darwin\" && ! \"$nonstop\" ; then\n    case $MAX_FD in #(\n      max*)\n        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        MAX_FD=$( ulimit -H -n ) ||\n            warn \"Could not query maximum file descriptor limit\"\n    esac\n    case $MAX_FD in  #(\n      '' | soft) :;; #(\n      *)\n        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.\n        # shellcheck disable=SC2039,SC3045\n        ulimit -n \"$MAX_FD\" ||\n            warn \"Could not set maximum file descriptor limit to $MAX_FD\"\n    esac\nfi\n\n# Collect all arguments for the java command, stacking in reverse order:\n#   * args from the command line\n#   * the main class name\n#   * -classpath\n#   * -D...appname settings\n#   * --module-path (only if needed)\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif \"$cygwin\" || \"$msys\" ; then\n    APP_HOME=$( cygpath --path --mixed \"$APP_HOME\" )\n    CLASSPATH=$( cygpath --path --mixed \"$CLASSPATH\" )\n\n    JAVACMD=$( cygpath --unix \"$JAVACMD\" )\n\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    for arg do\n        if\n            case $arg in                                #(\n              -*)   false ;;                            # don't mess with options #(\n              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath\n                    [ -e \"$t\" ] ;;                      #(\n              *)    false ;;\n            esac\n        then\n            arg=$( cygpath --path --ignore --mixed \"$arg\" )\n        fi\n        # Roll the args list around exactly as many times as the number of\n        # args, so each arg winds up back in the position where it started, but\n        # possibly modified.\n        #\n        # NB: a `for` loop captures its iteration list before it begins, so\n        # changing the positional parameters here affects neither the number of\n        # iterations, nor the values presented in `arg`.\n        shift                   # remove old arg\n        set -- \"$@\" \"$arg\"      # push replacement arg\n    done\nfi\n\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Collect all arguments for the java command:\n#   * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,\n#     and any embedded shellness will be escaped.\n#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be\n#     treated as '${Hostname}' itself on the command line.\n\nset -- \\\n        \"-Dorg.gradle.appname=$APP_BASE_NAME\" \\\n        -classpath \"$CLASSPATH\" \\\n        org.gradle.wrapper.GradleWrapperMain \\\n        \"$@\"\n\n# Stop when \"xargs\" is not available.\nif ! command -v xargs >/dev/null 2>&1\nthen\n    die \"xargs is not available\"\nfi\n\n# Use \"xargs\" to parse quoted args.\n#\n# With -n1 it outputs one arg per line, with the quotes and backslashes removed.\n#\n# In Bash we could simply go:\n#\n#   readarray ARGS < <( xargs -n1 <<<\"$var\" ) &&\n#   set -- \"${ARGS[@]}\" \"$@\"\n#\n# but POSIX shell has neither arrays nor command substitution, so instead we\n# post-process each arg (as a line of input to sed) to backslash-escape any\n# character that might be a shell metacharacter, then use eval to reverse\n# that process (while maintaining the separation between arguments), and wrap\n# the whole thing up as a single \"set\" statement.\n#\n# This will of course break if any of these variables contains a newline or\n# an unmatched quote.\n#\n\neval \"set -- $(\n        printf '%s\\n' \"$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\" |\n        xargs -n1 |\n        sed ' s~[^-[:alnum:]+,./:=@_]~\\\\&~g; ' |\n        tr '\\n' ' '\n    )\" '\"$@\"'\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\r\n@rem Copyright 2015 the original author or authors.\r\n@rem\r\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\r\n@rem you may not use this file except in compliance with the License.\r\n@rem You may obtain a copy of the License at\r\n@rem\r\n@rem      https://www.apache.org/licenses/LICENSE-2.0\r\n@rem\r\n@rem Unless required by applicable law or agreed to in writing, software\r\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\r\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r\n@rem See the License for the specific language governing permissions and\r\n@rem limitations under the License.\r\n@rem\r\n@rem SPDX-License-Identifier: Apache-2.0\r\n@rem\r\n\r\n@if \"%DEBUG%\"==\"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\"==\"\" set DIRNAME=.\r\n@rem This is normally unused\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Resolve any \".\" and \"..\" in APP_HOME to make it shorter.\r\nfor %%i in (\"%APP_HOME%\") do set APP_HOME=%%~fi\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif %ERRORLEVEL% equ 0 goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto execute\r\n\r\necho. 1>&2\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2\r\necho. 1>&2\r\necho Please set the JAVA_HOME variable in your environment to match the 1>&2\r\necho location of your Java installation. 1>&2\r\n\r\ngoto fail\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %*\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif %ERRORLEVEL% equ 0 goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif %EXIT_CODE% equ 0 set EXIT_CODE=1\r\nif not \"\"==\"%GRADLE_EXIT_CONSOLE%\" exit %EXIT_CODE%\r\nexit /b %EXIT_CODE%\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "rootProject.name = 'temporal-java-samples'\ninclude 'core'\ninclude 'springai:basic'\ninclude 'springai:mcp'\ninclude 'springai:multimodel'\ninclude 'springai:rag'\ninclude 'springboot'\ninclude 'springboot-basic'\n\n"
  },
  {
    "path": "springai/basic/build.gradle",
    "content": "apply from: \"$rootDir/gradle/springai.gradle\"\n\ndependencies {\n    implementation 'org.springframework.ai:spring-ai-starter-model-openai'\n}\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/ChatExampleApplication.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport java.util.Scanner;\nimport java.util.UUID;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.context.event.EventListener;\nimport org.springframework.stereotype.Component;\n\n/**\n * Example application demonstrating the Spring AI Temporal plugin.\n *\n * <p>Starts an interactive chat workflow where each AI call is a durable Temporal activity with\n * automatic retries and timeout handling.\n */\n@SpringBootApplication\npublic class ChatExampleApplication {\n\n  public static void main(String[] args) {\n    SpringApplication.run(ChatExampleApplication.class, args);\n  }\n}\n\n@Component\nclass ChatRunner {\n\n  private final WorkflowClient workflowClient;\n\n  ChatRunner(WorkflowClient workflowClient) {\n    this.workflowClient = workflowClient;\n  }\n\n  @EventListener(ApplicationReadyEvent.class)\n  public void run() {\n    String workflowId = \"chat-\" + UUID.randomUUID().toString().substring(0, 8);\n\n    System.out.println(\"\\n===========================================\");\n    System.out.println(\"  Spring AI + Temporal Chat Demo\");\n    System.out.println(\"===========================================\");\n    System.out.println(\"Workflow ID: \" + workflowId);\n    System.out.println(\"Type messages, or 'quit' to exit.\\n\");\n\n    // Start the chat workflow\n    ChatWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            ChatWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setWorkflowId(workflowId)\n                .setTaskQueue(\"spring-ai-example\")\n                .build());\n\n    WorkflowClient.start(workflow::run, \"You are a helpful assistant. Be concise.\");\n\n    // Get stub for the running workflow\n    ChatWorkflow chat = workflowClient.newWorkflowStub(ChatWorkflow.class, workflowId);\n\n    // Interactive loop\n    try (Scanner scanner = new Scanner(System.in, java.nio.charset.StandardCharsets.UTF_8)) {\n      while (true) {\n        System.out.print(\"You: \");\n        String input = scanner.nextLine().trim();\n\n        if (input.equalsIgnoreCase(\"quit\") || input.equalsIgnoreCase(\"exit\")) {\n          chat.end();\n          break;\n        }\n\n        if (input.isEmpty()) {\n          continue;\n        }\n\n        try {\n          String response = chat.chat(input);\n          System.out.println(\"Assistant: \" + response + \"\\n\");\n        } catch (Exception e) {\n          System.err.println(\"Error: \" + e.getMessage() + \"\\n\");\n        }\n      }\n    }\n\n    System.out.println(\"Goodbye!\");\n    System.exit(0);\n  }\n}\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/ChatWorkflow.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.UpdateMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * A chat workflow that maintains a conversation with an AI model.\n *\n * <p>The workflow runs until explicitly ended via the {@link #end()} signal. Messages can be sent\n * via the {@link #chat(String)} update method, which returns the AI's response synchronously.\n */\n@WorkflowInterface\npublic interface ChatWorkflow {\n\n  /**\n   * Starts the chat workflow and waits until ended.\n   *\n   * @param systemPrompt the system prompt that defines the AI's behavior\n   * @return a summary when the chat ends\n   */\n  @WorkflowMethod\n  String run(String systemPrompt);\n\n  /**\n   * Sends a message to the AI and returns its response.\n   *\n   * @param message the user's message\n   * @return the AI's response\n   */\n  @UpdateMethod\n  String chat(String message);\n\n  /** Ends the chat session. */\n  @SignalMethod\n  void end();\n}\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/ChatWorkflowImpl.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.springai.chat.TemporalChatClient;\nimport io.temporal.springai.model.ActivityChatModel;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInit;\nimport java.time.Duration;\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;\nimport org.springframework.ai.chat.memory.ChatMemory;\nimport org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;\nimport org.springframework.ai.chat.memory.MessageWindowChatMemory;\n\n/**\n * Implementation of the chat workflow using Spring AI's ChatClient with Temporal tools.\n *\n * <p>This demonstrates how to use the Spring AI plugin within a Temporal workflow:\n *\n * <ol>\n *   <li>Build an {@link ActivityChatModel} via its factory to get a standard Spring AI ChatModel\n *       backed by a durable Temporal activity\n *   <li>Create activity stubs for tools (e.g., {@link WeatherActivity})\n *   <li>Create deterministic tools (e.g., {@link StringTools})\n *   <li>Create side-effect tools (e.g., {@link TimestampTools})\n *   <li>Use {@link TemporalChatClient} to build a tool-aware chat client\n * </ol>\n *\n * <p>The AI model can call:\n *\n * <ul>\n *   <li>{@code getWeather(city)} - Executes as a durable Temporal activity\n *   <li>{@code getForecast(city, days)} - Executes as a durable Temporal activity\n *   <li>{@code reverse(text)}, {@code countWords(text)}, etc. - Execute directly in workflow (plain\n *       workflow tool)\n *   <li>{@code getCurrentDateTime()}, {@code generateUuid()}, etc. - Wrapped in sideEffect\n *       (@SideEffectTool)\n * </ul>\n */\npublic class ChatWorkflowImpl implements ChatWorkflow {\n\n  private final ChatClient chatClient;\n  private boolean ended = false;\n  private int messageCount = 0;\n\n  // @@@SNIPSTART samples-java-spring-ai-chat-workflow-init\n  @WorkflowInit\n  public ChatWorkflowImpl(String systemPrompt) {\n    // Build an activity-backed chat model. The factory creates the activity stub\n    // internally and registers per-call Summaries on the Temporal UI.\n    ActivityChatModel activityChatModel = ActivityChatModel.forDefault();\n\n    // Create an activity stub for weather tools - these execute as durable activities\n    WeatherActivity weatherTool =\n        Workflow.newActivityStub(\n            WeatherActivity.class,\n            ActivityOptions.newBuilder()\n                .setStartToCloseTimeout(Duration.ofSeconds(30))\n                .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build())\n                .build());\n\n    // Create deterministic tools - these execute directly in the workflow\n    StringTools stringTools = new StringTools();\n\n    // Create side-effect tools - these are wrapped in Workflow.sideEffect()\n    // The result is recorded in history, making replay deterministic\n    TimestampTools timestampTools = new TimestampTools();\n\n    // Create chat memory - uses in-memory storage that gets rebuilt on replay\n    ChatMemory chatMemory =\n        MessageWindowChatMemory.builder()\n            .chatMemoryRepository(new InMemoryChatMemoryRepository())\n            .maxMessages(20)\n            .build();\n\n    // Build a TemporalChatClient with tools and memory\n    // - Activity stubs (weatherTool) become durable AI tools\n    // - plain workflow tool classes (stringTools) execute directly in workflow\n    // - @SideEffectTool classes (timestampTools) are wrapped in sideEffect()\n    // - PromptChatMemoryAdvisor maintains conversation history\n    this.chatClient =\n        TemporalChatClient.builder(activityChatModel)\n            .defaultSystem(systemPrompt)\n            .defaultTools(weatherTool, stringTools, timestampTools)\n            .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())\n            .build();\n  }\n\n  // @@@SNIPEND\n\n  @Override\n  public String run(String systemPrompt) {\n    // systemPrompt is unused here on purpose — @WorkflowInit requires the constructor\n    // and the @WorkflowMethod to share a parameter list, and the constructor above\n    // already consumed it to build the chat client.\n    Workflow.await(() -> ended);\n    return \"Chat ended after \" + messageCount + \" messages.\";\n  }\n\n  @Override\n  public String chat(String message) {\n    messageCount++;\n    return chatClient.prompt().user(message).call().content();\n  }\n\n  @Override\n  public void end() {\n    ended = true;\n  }\n}\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/StringTools.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.ai.tool.annotation.ToolParam;\n\n/**\n * Deterministic string manipulation tools.\n *\n * <p>These tools execute directly in workflow context. Since they are pure functions (same output\n * for same input, no side effects), they are safe for Temporal replay without any wrapping.\n */\n// @@@SNIPSTART samples-java-spring-ai-plain-tool\npublic class StringTools {\n\n  @Tool(description = \"Reverse a string, returning the characters in opposite order\")\n  public String reverse(@ToolParam(description = \"The string to reverse\") String input) {\n    if (input == null) {\n      return null;\n    }\n    return new StringBuilder(input).reverse().toString();\n  }\n\n  @Tool(description = \"Count the number of words in a text\")\n  public int countWords(@ToolParam(description = \"The text to count words in\") String text) {\n    if (text == null || text.isBlank()) {\n      return 0;\n    }\n    return text.trim().split(\"\\\\s+\").length;\n  }\n\n  @Tool(description = \"Convert text to all uppercase letters\")\n  public String toUpperCase(@ToolParam(description = \"The text to convert\") String text) {\n    if (text == null) {\n      return null;\n    }\n    return text.toUpperCase(java.util.Locale.ROOT);\n  }\n\n  @Tool(description = \"Convert text to all lowercase letters\")\n  public String toLowerCase(@ToolParam(description = \"The text to convert\") String text) {\n    if (text == null) {\n      return null;\n    }\n    return text.toLowerCase(java.util.Locale.ROOT);\n  }\n\n  @Tool(description = \"Check if a string is a palindrome (reads the same forwards and backwards)\")\n  public boolean isPalindrome(@ToolParam(description = \"The text to check\") String text) {\n    if (text == null) {\n      return false;\n    }\n    String normalized = text.toLowerCase(java.util.Locale.ROOT).replaceAll(\"\\\\s+\", \"\");\n    String reversed = new StringBuilder(normalized).reverse().toString();\n    return normalized.equals(reversed);\n  }\n}\n// @@@SNIPEND\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/TimestampTools.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport io.temporal.springai.tool.SideEffectTool;\nimport java.time.Instant;\nimport java.time.ZoneId;\nimport java.time.format.DateTimeFormatter;\nimport java.util.UUID;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.ai.tool.annotation.ToolParam;\n\n/**\n * Side-effect tools that return non-deterministic values.\n *\n * <p>This class demonstrates the use of {@link SideEffectTool} annotation for tools that are\n * non-deterministic but don't need the full durability of an activity.\n *\n * <p>Side-effect tools are wrapped in {@code Workflow.sideEffect()}, which:\n *\n * <ul>\n *   <li>Records the result in workflow history on first execution\n *   <li>Returns the recorded result on replay (deterministic)\n *   <li>Does not create activity tasks (lightweight)\n * </ul>\n *\n * <p>Use {@code @SideEffectTool} for:\n *\n * <ul>\n *   <li>Getting current time/date\n *   <li>Generating random values (UUIDs, random numbers)\n *   <li>Any non-deterministic operation that doesn't need retry/durability\n * </ul>\n *\n * <p>Example usage:\n *\n * <pre>{@code\n * TimestampTools timestampTools = new TimestampTools();\n * this.chatClient = TemporalChatClient.builder(activityChatModel)\n *         .defaultTools(timestampTools)  // Wrapped in sideEffect()\n *         .build();\n * }</pre>\n */\n// @@@SNIPSTART samples-java-spring-ai-side-effect-tool\n@SideEffectTool\npublic class TimestampTools {\n\n  private static final DateTimeFormatter FORMATTER =\n      DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss z\").withZone(ZoneId.systemDefault());\n\n  /**\n   * Gets the current date and time.\n   *\n   * <p>This is non-deterministic (returns different values each time), but wrapped in sideEffect()\n   * it becomes safe for workflow replay.\n   *\n   * @return the current date and time as a formatted string\n   */\n  @Tool(description = \"Get the current date and time\")\n  public String getCurrentDateTime() {\n    return FORMATTER.format(Instant.now());\n  }\n\n  /**\n   * Gets the current Unix timestamp in milliseconds.\n   *\n   * @return the current time in milliseconds since epoch\n   */\n  @Tool(description = \"Get the current Unix timestamp in milliseconds\")\n  public long getCurrentTimestamp() {\n    return System.currentTimeMillis();\n  }\n\n  /**\n   * Generates a random UUID.\n   *\n   * @return a new random UUID string\n   */\n  @Tool(description = \"Generate a random UUID\")\n  public String generateUuid() {\n    return UUID.randomUUID().toString();\n  }\n\n  /**\n   * Gets the current date and time in a specific timezone.\n   *\n   * @param timezone the timezone ID (e.g., \"America/New_York\", \"UTC\", \"Europe/London\")\n   * @return the current date and time in the specified timezone\n   */\n  @Tool(description = \"Get the current date and time in a specific timezone\")\n  public String getDateTimeInTimezone(\n      @ToolParam(description = \"Timezone ID (e.g., 'America/New_York', 'UTC', 'Europe/London')\")\n          String timezone) {\n    try {\n      ZoneId zoneId = ZoneId.of(timezone);\n      DateTimeFormatter formatter =\n          DateTimeFormatter.ofPattern(\"yyyy-MM-dd HH:mm:ss z\").withZone(zoneId);\n      return formatter.format(Instant.now());\n    } catch (Exception e) {\n      return \"Invalid timezone: \" + timezone + \". Use formats like 'America/New_York' or 'UTC'.\";\n    }\n  }\n}\n// @@@SNIPEND\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/WeatherActivity.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.activity.ActivityMethod;\nimport org.springframework.ai.tool.annotation.Tool;\nimport org.springframework.ai.tool.annotation.ToolParam;\n\n/**\n * Activity interface for weather-related operations.\n *\n * <p>This demonstrates how to combine Temporal's {@link ActivityInterface} with Spring AI's {@link\n * Tool} annotation to create activity-based AI tools.\n *\n * <p>When passed to {@code TemporalChatClient.builder().defaultTools(weatherActivity)}, the AI\n * model can call these methods, and they will execute as durable Temporal activities with automatic\n * retries and timeout handling.\n */\n// @@@SNIPSTART samples-java-spring-ai-activity-tool\n@ActivityInterface\npublic interface WeatherActivity {\n\n  /**\n   * Gets the current weather for a city.\n   *\n   * <p>The {@code @Tool} annotation makes this method available to the AI model, while the\n   * {@code @ActivityInterface} ensures it executes as a Temporal activity.\n   *\n   * @param city the name of the city\n   * @return a description of the current weather\n   */\n  @Tool(\n      description =\n          \"Get the current weather for a city. Returns temperature, conditions, and humidity.\")\n  @ActivityMethod\n  String getWeather(\n      @ToolParam(description = \"The name of the city (e.g., 'Seattle', 'New York')\") String city);\n\n  /**\n   * Gets the weather forecast for a city.\n   *\n   * @param city the name of the city\n   * @param days the number of days to forecast (1-7)\n   * @return the weather forecast\n   */\n  @Tool(description = \"Get the weather forecast for a city for the specified number of days.\")\n  @ActivityMethod\n  String getForecast(\n      @ToolParam(description = \"The name of the city\") String city,\n      @ToolParam(description = \"Number of days to forecast (1-7)\") int days);\n}\n// @@@SNIPEND\n"
  },
  {
    "path": "springai/basic/src/main/java/io/temporal/samples/springai/chat/WeatherActivityImpl.java",
    "content": "package io.temporal.samples.springai.chat;\n\nimport java.util.Map;\nimport java.util.Random;\nimport org.springframework.stereotype.Component;\n\n/**\n * Implementation of {@link WeatherActivity}.\n *\n * <p>This is a mock implementation that returns simulated weather data. In a real application, this\n * would call an external weather API.\n *\n * <p>Note: This class is registered as a Spring {@code @Component} so it can be auto-discovered.\n * The {@code SpringAiPlugin} will register it with Temporal workers.\n */\n@Component\npublic class WeatherActivityImpl implements WeatherActivity {\n\n  // Mock weather data for demo purposes\n  private static final Map<String, String[]> WEATHER_DATA =\n      Map.of(\n          \"seattle\", new String[] {\"Rainy\", \"55\"},\n          \"new york\", new String[] {\"Partly Cloudy\", \"62\"},\n          \"los angeles\", new String[] {\"Sunny\", \"78\"},\n          \"chicago\", new String[] {\"Windy\", \"48\"},\n          \"miami\", new String[] {\"Hot and Humid\", \"88\"},\n          \"denver\", new String[] {\"Clear\", \"45\"},\n          \"boston\", new String[] {\"Overcast\", \"52\"});\n\n  @Override\n  public String getWeather(String city) {\n    String normalizedCity = city.toLowerCase(java.util.Locale.ROOT).trim();\n    String[] weather = WEATHER_DATA.getOrDefault(normalizedCity, new String[] {\"Unknown\", \"60\"});\n\n    int humidity = 40 + new Random().nextInt(40); // 40-80%\n\n    return String.format(\n        \"Weather in %s: %s, Temperature: %s°F, Humidity: %d%%\",\n        city, weather[0], weather[1], humidity);\n  }\n\n  @Override\n  public String getForecast(String city, int days) {\n    if (days < 1 || days > 7) {\n      return \"Error: Days must be between 1 and 7\";\n    }\n\n    StringBuilder forecast = new StringBuilder();\n    forecast.append(String.format(\"%d-day forecast for %s:\\n\", days, city));\n\n    String[] conditions = {\"Sunny\", \"Partly Cloudy\", \"Cloudy\", \"Rainy\", \"Clear\"};\n    Random random = new Random();\n\n    for (int i = 1; i <= days; i++) {\n      String condition = conditions[random.nextInt(conditions.length)];\n      int high = 50 + random.nextInt(30);\n      int low = high - 10 - random.nextInt(10);\n      forecast.append(\n          String.format(\"  Day %d: %s, High: %d°F, Low: %d°F\\n\", i, condition, high, low));\n    }\n\n    return forecast.toString();\n  }\n}\n"
  },
  {
    "path": "springai/basic/src/main/resources/application.yaml",
    "content": "spring:\n  application:\n    name: spring-ai-temporal-example\n  main:\n    web-application-type: none\n  ai:\n    openai:\n      api-key: ${OPENAI_API_KEY}\n      chat:\n        options:\n          model: gpt-4o-mini\n          temperature: 0.7\n  temporal:\n    connection:\n      target: localhost:7233\n    workers:\n      - task-queue: spring-ai-example\n        workflow-classes:\n          - io.temporal.samples.springai.chat.ChatWorkflowImpl\n        activity-beans:\n          - weatherActivityImpl\n\nlogging:\n  level:\n    io.temporal.springai: DEBUG\n"
  },
  {
    "path": "springai/mcp/build.gradle",
    "content": "apply from: \"$rootDir/gradle/springai.gradle\"\n\ndependencies {\n    implementation 'org.springframework.ai:spring-ai-starter-model-openai'\n    implementation 'org.springframework.ai:spring-ai-starter-mcp-client'\n    implementation 'org.springframework.ai:spring-ai-rag'\n    implementation 'org.springframework.boot:spring-boot-starter-webflux'\n}\n"
  },
  {
    "path": "springai/mcp/src/main/java/io/temporal/samples/springai/mcp/McpApplication.java",
    "content": "package io.temporal.samples.springai.mcp;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\nimport java.util.Scanner;\nimport java.util.UUID;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\nimport org.springframework.boot.context.event.ApplicationReadyEvent;\nimport org.springframework.context.event.EventListener;\n\n/**\n * Example application demonstrating MCP (Model Context Protocol) integration.\n *\n * <p>This application shows how to use tools from MCP servers within Temporal workflows. It\n * connects to a filesystem MCP server and provides an AI assistant that can read and write files.\n *\n * <h2>Usage</h2>\n *\n * <pre>\n * Commands:\n *   tools                - List available MCP tools\n *   &lt;any message&gt;       - Chat with the AI (it can use file tools)\n *   quit                 - End the chat\n * </pre>\n *\n * <h2>Example Interactions</h2>\n *\n * <pre>\n * &gt; List files in the current directory\n * [AI uses list_directory tool and returns results]\n *\n * &gt; Create a file called hello.txt with \"Hello from MCP!\"\n * [AI uses write_file tool]\n *\n * &gt; Read the contents of hello.txt\n * [AI uses read_file tool]\n * </pre>\n *\n * <h2>Prerequisites</h2>\n *\n * <ol>\n *   <li>Start a Temporal dev server: {@code temporal server start-dev}\n *   <li>Set OPENAI_API_KEY environment variable\n *   <li>Ensure Node.js/npx is available (for MCP server)\n *   <li>Optionally set MCP_ALLOWED_PATH (defaults to /tmp/mcp-example)\n *   <li>Run: {@code ./gradlew :springai:mcp:bootRun}\n * </ol>\n */\n@SpringBootApplication\npublic class McpApplication {\n\n  private static final String TASK_QUEUE = \"mcp-example-queue\";\n\n  @Autowired private WorkflowClient workflowClient;\n\n  public static void main(String[] args) throws Exception {\n    // The filesystem MCP server refuses to start if the allowed path doesn't\n    // exist, so create it up front. Must happen before SpringApplication.run —\n    // the MCP client connects during context startup.\n    String allowedPath = System.getenv().getOrDefault(\"MCP_ALLOWED_PATH\", \"/tmp/mcp-example\");\n    Files.createDirectories(Path.of(allowedPath));\n\n    SpringApplication.run(McpApplication.class, args);\n  }\n\n  /** Runs after workers are started (ApplicationReadyEvent fires after CommandLineRunner). */\n  @EventListener(ApplicationReadyEvent.class)\n  public void onReady() throws Exception {\n    // Start a new workflow\n    String workflowId = \"mcp-example-\" + UUID.randomUUID().toString().substring(0, 8);\n    McpWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            McpWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(TASK_QUEUE)\n                .setWorkflowId(workflowId)\n                .build());\n\n    // Start the workflow asynchronously\n    WorkflowClient.start(workflow::run);\n\n    // Give the workflow time to initialize (first workflow task must complete)\n    Thread.sleep(1000);\n\n    System.out.println(\"\\n=== MCP Tools Demo ===\");\n    System.out.println(\"Workflow ID: \" + workflowId);\n    System.out.println(\"\\nThis demo uses the filesystem MCP server.\");\n    System.out.println(\"The AI can read, write, and list files in the allowed directory.\");\n    System.out.println(\"\\nCommands:\");\n    System.out.println(\"  tools    - List available MCP tools\");\n    System.out.println(\"  <text>   - Chat with the AI\");\n    System.out.println(\"  quit     - End the chat\");\n    System.out.println();\n\n    // Get a workflow stub for sending signals/queries\n    McpWorkflow workflowStub = workflowClient.newWorkflowStub(McpWorkflow.class, workflowId);\n\n    // Note: tools command may take a moment to work while workflow initializes\n    System.out.println(\"Type 'tools' to list available MCP tools.\\n\");\n\n    Scanner scanner = new Scanner(System.in, java.nio.charset.StandardCharsets.UTF_8);\n    while (true) {\n      System.out.print(\"> \");\n      String input = scanner.nextLine().trim();\n\n      if (input.equalsIgnoreCase(\"quit\")) {\n        workflowStub.end();\n        System.out.println(\"Chat ended. Goodbye!\");\n        break;\n      }\n\n      if (input.equalsIgnoreCase(\"tools\")) {\n        System.out.println(workflowStub.listTools());\n        continue;\n      }\n\n      if (input.isEmpty()) {\n        continue;\n      }\n\n      System.out.println(\"[Processing...]\");\n\n      // Capture current response BEFORE sending, so we can detect when it changes\n      String previousResponse = workflowStub.getLastResponse();\n\n      // Send the message via signal\n      workflowStub.chat(input);\n\n      // Poll until the response changes (workflow has processed our message)\n      for (int i = 0; i < 600; i++) { // Wait up to 60 seconds (MCP tools can be slow)\n        String response = workflowStub.getLastResponse();\n        if (!response.equals(previousResponse)) {\n          System.out.println(\"\\n[AI]: \" + response + \"\\n\");\n          break;\n        }\n        Thread.sleep(100);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "springai/mcp/src/main/java/io/temporal/samples/springai/mcp/McpWorkflow.java",
    "content": "package io.temporal.samples.springai.mcp;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * Workflow interface demonstrating MCP (Model Context Protocol) integration.\n *\n * <p>This workflow shows how to use tools from MCP servers within Temporal workflows. The AI model\n * can call MCP tools (like file system operations) as durable activities.\n */\n@WorkflowInterface\npublic interface McpWorkflow {\n\n  /**\n   * Runs the workflow until ended.\n   *\n   * @return summary of the chat session\n   */\n  @WorkflowMethod\n  String run();\n\n  /**\n   * Sends a message to the AI assistant with MCP tools available.\n   *\n   * @param message the user message\n   */\n  @SignalMethod\n  void chat(String message);\n\n  /**\n   * Gets the last response from the AI.\n   *\n   * @return the last response\n   */\n  @QueryMethod\n  String getLastResponse();\n\n  /**\n   * Lists the available MCP tools.\n   *\n   * @return list of available tools\n   */\n  @QueryMethod\n  String listTools();\n\n  /** Ends the chat session. */\n  @SignalMethod\n  void end();\n}\n"
  },
  {
    "path": "springai/mcp/src/main/java/io/temporal/samples/springai/mcp/McpWorkflowImpl.java",
    "content": "package io.temporal.samples.springai.mcp;\n\nimport io.temporal.springai.chat.TemporalChatClient;\nimport io.temporal.springai.mcp.ActivityMcpClient;\nimport io.temporal.springai.mcp.McpToolCallback;\nimport io.temporal.springai.model.ActivityChatModel;\nimport io.temporal.workflow.Workflow;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.springframework.ai.chat.client.ChatClient;\nimport org.springframework.ai.chat.client.advisor.PromptChatMemoryAdvisor;\nimport org.springframework.ai.chat.memory.ChatMemory;\nimport org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;\nimport org.springframework.ai.chat.memory.MessageWindowChatMemory;\nimport org.springframework.ai.tool.ToolCallback;\n\n/**\n * Implementation of the MCP workflow.\n *\n * <p>This demonstrates how to use MCP tools from external servers within a Temporal workflow. The\n * workflow:\n *\n * <ol>\n *   <li>Creates an MCP client that wraps the McpClientActivity\n *   <li>Discovers tools from connected MCP servers\n *   <li>Registers those tools with the chat client\n *   <li>When the AI calls an MCP tool, it executes as a durable activity\n * </ol>\n *\n * <p>This example uses the filesystem MCP server which provides tools like:\n *\n * <ul>\n *   <li>{@code read_file} - Read contents of a file\n *   <li>{@code write_file} - Write content to a file\n *   <li>{@code list_directory} - List directory contents\n *   <li>{@code create_directory} - Create a new directory\n * </ul>\n */\npublic class McpWorkflowImpl implements McpWorkflow {\n\n  private ChatClient chatClient;\n  private List<ToolCallback> mcpTools;\n  private String lastResponse = \"\";\n  private boolean ended = false;\n  private int messageCount = 0;\n  private boolean initialized = false;\n\n  @Override\n  public String run() {\n    // Discover MCP tools at the start of workflow execution (not in constructor)\n    // This avoids the \"root workflow thread yielding\" warning\n    ActivityMcpClient mcpClient = ActivityMcpClient.create();\n    mcpTools = McpToolCallback.fromMcpClient(mcpClient);\n\n    // Create the chat model (uses the default ChatModel bean)\n    ActivityChatModel chatModel = ActivityChatModel.forDefault();\n\n    // Create chat memory - uses in-memory storage that gets rebuilt on replay\n    // since the same messages will be added in the same order\n    ChatMemory chatMemory =\n        MessageWindowChatMemory.builder()\n            .chatMemoryRepository(new InMemoryChatMemoryRepository())\n            .maxMessages(20)\n            .build();\n\n    // Build the chat client with MCP tools and memory advisor\n    this.chatClient =\n        TemporalChatClient.builder(chatModel)\n            .defaultSystem(\n                \"\"\"\n                You are a helpful assistant with access to file system tools.\n                You can read files, write files, list directories, and more.\n\n                When asked to perform file operations, use your tools.\n                Always confirm what you did after completing an operation.\n                \"\"\")\n            .defaultToolCallbacks(mcpTools)\n            .defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())\n            .build();\n\n    initialized = true;\n\n    // Wait until the chat is ended\n    Workflow.await(() -> ended);\n    return \"Chat ended after \" + messageCount + \" messages.\";\n  }\n\n  @Override\n  public void chat(String message) {\n    // The signal can land before run() has finished setting up the chat client (MCP tool\n    // discovery is an activity, so initialization yields). Block in workflow time until\n    // init completes, then process the message — the client's signal RPC has already\n    // returned, so this only delays the workflow-side handling.\n    Workflow.await(() -> initialized);\n\n    messageCount++;\n\n    // PromptChatMemoryAdvisor automatically handles conversation history\n    lastResponse = chatClient.prompt().user(message).call().content();\n  }\n\n  @Override\n  public String getLastResponse() {\n    return lastResponse;\n  }\n\n  @Override\n  public String listTools() {\n    if (!initialized || mcpTools == null) {\n      return \"Workflow is still initializing. Please wait a moment and try again.\";\n    }\n\n    if (mcpTools.isEmpty()) {\n      return \"No MCP tools available. Check MCP server configuration.\";\n    }\n\n    return mcpTools.stream()\n        .map(\n            tool ->\n                \" - \"\n                    + tool.getToolDefinition().name()\n                    + \": \"\n                    + tool.getToolDefinition().description())\n        .collect(Collectors.joining(\"\\n\", \"Available MCP tools:\\n\", \"\"));\n  }\n\n  @Override\n  public void end() {\n    ended = true;\n  }\n}\n"
  },
  {
    "path": "springai/mcp/src/main/resources/application.yaml",
    "content": "spring:\n  main:\n    banner-mode: off\n    web-application-type: none\n  ai:\n    openai:\n      api-key: ${OPENAI_API_KEY}\n      chat:\n        options:\n          model: gpt-4o-mini\n    mcp:\n      client:\n        stdio:\n          connections:\n            # Filesystem MCP server - provides read_file, write_file, list_directory tools\n            filesystem:\n              command: npx\n              args:\n                - \"-y\"\n                - \"@modelcontextprotocol/server-filesystem\"\n                - \"${MCP_ALLOWED_PATH:/tmp/mcp-example}\"\n\n  temporal:\n    connection:\n      target: localhost:7233\n    workers:\n      - task-queue: mcp-example-queue\n        workflow-classes:\n          - io.temporal.samples.springai.mcp.McpWorkflowImpl\n\nlogging:\n  level:\n    io.temporal.springai: DEBUG\n"
  },
  {
    "path": "springai/multimodel/build.gradle",
    "content": "apply from: \"$rootDir/gradle/springai.gradle\"\n\ndependencies {\n    implementation 'org.springframework.ai:spring-ai-starter-model-openai'\n    implementation 'org.springframework.ai:spring-ai-starter-model-anthropic'\n}\n"
  },
  {
    "path": "springai/multimodel/src/main/java/io/temporal/samples/springai/multimodel/ChatModelConfig.java",
    "content": "package io.temporal.samples.springai.multimodel;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.springai.autoconfigure.ChatModelActivityOptions;\nimport io.temporal.springai.model.ActivityChatModel;\nimport java.time.Duration;\nimport java.util.Map;\nimport org.springframework.ai.anthropic.AnthropicChatModel;\nimport org.springframework.ai.anthropic.AnthropicChatOptions;\nimport org.springframework.ai.anthropic.api.AnthropicApi;\nimport org.springframework.ai.chat.model.ChatModel;\nimport org.springframework.ai.openai.OpenAiChatModel;\nimport org.springframework.ai.openai.OpenAiChatOptions;\nimport org.springframework.ai.openai.api.OpenAiApi;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Primary;\n\n/**\n * Configuration for multiple chat models from different providers.\n *\n * <p>This demonstrates how to configure multiple AI providers in a Spring Boot application. Each\n * model is registered as a separate bean with a unique name.\n *\n * <p>In workflows, these can be accessed via:\n *\n * <ul>\n *   <li>{@code ActivityChatModel.forDefault()} - Uses the @Primary model (openAiChatModel)\n *   <li>{@code ActivityChatModel.forModel(\"openAiChatModel\")} - Uses OpenAI gpt-4o-mini\n *   <li>{@code ActivityChatModel.forModel(\"anthropicChatModel\")} - Uses Anthropic Claude\n * </ul>\n */\n@Configuration\npublic class ChatModelConfig {\n\n  @Value(\"${spring.ai.openai.api-key}\")\n  private String openAiApiKey;\n\n  @Value(\"${spring.ai.anthropic.api-key}\")\n  private String anthropicApiKey;\n\n  /**\n   * OpenAI model using gpt-4o-mini for quick, cost-effective responses. Marked as @Primary so it's\n   * used when no specific model is requested.\n   */\n  @Bean\n  @Primary\n  public ChatModel openAiChatModel() {\n    OpenAiApi api = OpenAiApi.builder().apiKey(openAiApiKey).build();\n    OpenAiChatOptions options =\n        OpenAiChatOptions.builder().model(\"gpt-4o-mini\").temperature(0.7).build();\n    return OpenAiChatModel.builder().openAiApi(api).defaultOptions(options).build();\n  }\n\n  /** Anthropic model using Claude for complex reasoning tasks. */\n  @Bean\n  public ChatModel anthropicChatModel() {\n    AnthropicApi api = AnthropicApi.builder().apiKey(anthropicApiKey).build();\n    AnthropicChatOptions options =\n        AnthropicChatOptions.builder()\n            .model(\"claude-sonnet-4-20250514\")\n            .temperature(0.3) // Lower temperature for more focused reasoning\n            .build();\n    return AnthropicChatModel.builder().anthropicApi(api).defaultOptions(options).build();\n  }\n\n  /**\n   * Per-model {@link ActivityOptions} overrides, declared as a single Spring bean. When present,\n   * {@link ActivityChatModel#forModel(String)} and {@link ActivityChatModel#forDefault()} consult\n   * this map before falling back to the plugin's defaults — so workflows can build a\n   * fully-configured chat model with nothing more than {@code ActivityChatModel.forModel(name)}.\n   *\n   * <p>The Anthropic entry bumps the start-to-close timeout (reasoning models can take minutes) and\n   * caps the schedule-to-close so a stuck request can't keep re-attempting forever. Building on\n   * {@link ActivityChatModel#defaultActivityOptions()} preserves the plugin's\n   * non-retryable-AI-error classification without having to restate it.\n   *\n   * <p>The workflow still uses the per-call {@code ChatClient.defaultOptions(...)} path for things\n   * that change per prompt (see the {@code think:} route in {@code MultiModelWorkflowImpl} —\n   * extended thinking is enabled per call, not globally).\n   */\n  // @@@SNIPSTART samples-java-spring-ai-per-model-options\n  @Bean\n  public ChatModelActivityOptions chatModelActivityOptions() {\n    return new ChatModelActivityOptions(\n        Map.of(\n            \"anthropicChatModel\",\n            ActivityOptions.newBuilder(ActivityChatModel.defaultActivityOptions())\n                .setStartToCloseTimeout(Duration.ofMinutes(5))\n                .setScheduleToCloseTimeout(Duration.ofMinutes(15))\n                .build()));\n  }\n  // @@@SNIPEND\n}\n"
  },
  {
    "path": "springai/multimodel/src/main/java/io/temporal/samples/springai/multimodel/MultiModelApplication.java",
    "content": "package io.temporal.samples.springai.multimodel;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport java.util.Scanner;\nimport java.util.UUID;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Example application demonstrating multi-model support with different AI providers.\n *\n * <p>This application shows how to use different AI providers (OpenAI and Anthropic) within the\n * same Temporal workflow. It provides an interactive CLI where you can send messages to different\n * models.\n *\n * <h2>Usage</h2>\n *\n * <pre>\n * Commands:\n *   openai: &lt;message&gt;    - Send to OpenAI (gpt-4o-mini)\n *   anthropic: &lt;message&gt; - Send to Anthropic (Claude)\n *   think: &lt;message&gt;     - Send to Anthropic with extended thinking enabled\n *   &lt;message&gt;            - No prefix routes to the default model (the @Primary bean)\n *   quit                  - End the chat\n * </pre>\n *\n * <h2>Prerequisites</h2>\n *\n * <ol>\n *   <li>Start a Temporal dev server: {@code temporal server start-dev}\n *   <li>Set OPENAI_API_KEY environment variable\n *   <li>Set ANTHROPIC_API_KEY environment variable\n *   <li>Run: {@code ./gradlew :springai:multimodel:bootRun}\n * </ol>\n */\n@SpringBootApplication\npublic class MultiModelApplication implements CommandLineRunner {\n\n  private static final String TASK_QUEUE = \"multi-model-queue\";\n\n  @Autowired private WorkflowClient workflowClient;\n\n  public static void main(String[] args) {\n    SpringApplication.run(MultiModelApplication.class, args);\n  }\n\n  @Override\n  public void run(String... args) throws Exception {\n    // Start a new workflow\n    String workflowId = \"multi-model-\" + UUID.randomUUID().toString().substring(0, 8);\n    MultiModelWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            MultiModelWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(TASK_QUEUE)\n                .setWorkflowId(workflowId)\n                .build());\n\n    // Start the workflow asynchronously\n    WorkflowClient.start(workflow::run);\n\n    System.out.println(\"\\n=== Multi-Provider Chat Demo ===\");\n    System.out.println(\"Workflow ID: \" + workflowId);\n    System.out.println(\"\\nAvailable models:\");\n    System.out.println(\"  openai:    OpenAI gpt-4o-mini\");\n    System.out.println(\"  anthropic: Anthropic Claude\");\n    System.out.println(\"  think:     Anthropic Claude with extended thinking (per-call options)\");\n    System.out.println(\"  default:   no prefix — routes to the @Primary model (OpenAI)\");\n    System.out.println(\"\\nCommands:\");\n    System.out.println(\"  openai: <message>    - Send to OpenAI\");\n    System.out.println(\"  anthropic: <message> - Send to Anthropic\");\n    System.out.println(\"  think: <message>     - Send to Anthropic with extended thinking enabled\");\n    System.out.println(\"  <message>            - No prefix routes to the default model\");\n    System.out.println(\"  quit                 - End the chat\");\n    System.out.println();\n\n    // Get a workflow stub for sending signals\n    MultiModelWorkflow workflowStub =\n        workflowClient.newWorkflowStub(MultiModelWorkflow.class, workflowId);\n\n    Scanner scanner = new Scanner(System.in, java.nio.charset.StandardCharsets.UTF_8);\n    while (true) {\n      System.out.print(\"> \");\n      String input = scanner.nextLine().trim();\n\n      if (input.equalsIgnoreCase(\"quit\")) {\n        workflowStub.end();\n        System.out.println(\"Chat ended. Goodbye!\");\n        break;\n      }\n\n      // Parse the model and message\n      String modelName;\n      String message;\n\n      if (input.startsWith(\"openai:\")) {\n        modelName = \"openai\";\n        message = input.substring(7).trim();\n      } else if (input.startsWith(\"anthropic:\")) {\n        modelName = \"anthropic\";\n        message = input.substring(10).trim();\n      } else if (input.startsWith(\"think:\")) {\n        modelName = \"think\";\n        message = input.substring(6).trim();\n      } else {\n        // No prefix — route to the workflow's \"default\" model (the @Primary bean,\n        // resolved by ActivityChatModel.forDefault() inside the workflow).\n        modelName = \"default\";\n        message = input;\n      }\n\n      if (message.isEmpty()) {\n        System.out.println(\"Please enter a message.\");\n        continue;\n      }\n\n      // Capture current response BEFORE sending so we can detect the change against\n      // the pre-signal baseline (not against an empty string — the workflow may already\n      // hold the previous prompt's reply).\n      String previousResponse = workflowStub.getLastResponse();\n      System.out.println(\"[Sending to \" + modelName + \" model...]\");\n      workflowStub.chat(modelName, message);\n\n      // Poll until the response changes from the pre-signal baseline.\n      for (int i = 0; i < 300; i++) { // Wait up to 30 seconds\n        String response = workflowStub.getLastResponse();\n        if (!response.equals(previousResponse)) {\n          System.out.println(\n              \"\\n[\" + modelName.toUpperCase(java.util.Locale.ROOT) + \"]: \" + response + \"\\n\");\n          break;\n        }\n        Thread.sleep(100);\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "springai/multimodel/src/main/java/io/temporal/samples/springai/multimodel/MultiModelWorkflow.java",
    "content": "package io.temporal.samples.springai.multimodel;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * Workflow interface demonstrating multiple chat models.\n *\n * <p>This workflow shows how to use different AI models for different purposes within the same\n * workflow.\n */\n@WorkflowInterface\npublic interface MultiModelWorkflow {\n\n  /**\n   * Runs the workflow until ended.\n   *\n   * @return summary of the chat session\n   */\n  @WorkflowMethod\n  String run();\n\n  /**\n   * Sends a message to a specific model.\n   *\n   * @param modelName the name of the model to use (\"openai\", \"anthropic\", \"think\", or \"default\")\n   * @param message the user message\n   */\n  @SignalMethod\n  void chat(String modelName, String message);\n\n  /**\n   * Gets the last response.\n   *\n   * @return the last response from any model\n   */\n  @QueryMethod\n  String getLastResponse();\n\n  /** Ends the chat session. */\n  @SignalMethod\n  void end();\n}\n"
  },
  {
    "path": "springai/multimodel/src/main/java/io/temporal/samples/springai/multimodel/MultiModelWorkflowImpl.java",
    "content": "package io.temporal.samples.springai.multimodel;\n\nimport io.temporal.springai.chat.TemporalChatClient;\nimport io.temporal.springai.model.ActivityChatModel;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInit;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport org.springframework.ai.anthropic.AnthropicChatOptions;\nimport org.springframework.ai.anthropic.api.AnthropicApi;\nimport org.springframework.ai.chat.client.ChatClient;\n\n/**\n * Implementation of the multi-model workflow.\n *\n * <p>This demonstrates how to use multiple AI providers in a single workflow:\n *\n * <ul>\n *   <li><b>openai</b> - Uses OpenAI gpt-4o-mini for quick, cost-effective responses\n *   <li><b>anthropic</b> - Uses Anthropic Claude for complex reasoning tasks\n *   <li><b>think</b> - Uses Anthropic Claude with <i>extended thinking</i> enabled for hard\n *       problems. This demonstrates {@code temporal-spring-ai}'s provider-specific ChatOptions\n *       pass-through: an {@link AnthropicChatOptions} instance with a {@code thinking}\n *       configuration is attached per call, and every field survives the round-trip across the\n *       activity boundary.\n *   <li><b>default</b> - Uses the primary/default model (OpenAI)\n * </ul>\n *\n * <p>The workflow uses two patterns for wiring activity options:\n *\n * <ul>\n *   <li><b>Bean-based overrides (declarative, static)</b> — {@code ChatModelConfig} declares a\n *       {@code ChatModelActivityOptions} bean with per-model {@code ActivityOptions}. The workflow\n *       then just calls {@link ActivityChatModel#forDefault()} / {@link\n *       ActivityChatModel#forModel(String)}, which consult that bean automatically. That covers\n *       static per-model config (Anthropic needs a longer timeout because reasoning models are\n *       slow; OpenAI uses plugin defaults).\n *   <li><b>Per-call {@code ChatClient.defaultOptions(...)} (imperative, dynamic)</b> — the {@code\n *       think:} route builds an {@link AnthropicChatOptions} instance with {@code thinking=ENABLED}\n *       and attaches it via {@code defaultOptions(...)}. That exercises the plugin's\n *       provider-specific ChatOptions pass-through: every field survives the round-trip across the\n *       activity boundary.\n * </ul>\n */\npublic class MultiModelWorkflowImpl implements MultiModelWorkflow {\n\n  private final Map<String, ChatClient> chatClients;\n  private String lastResponse = \"\";\n  private boolean ended = false;\n  private int messageCount = 0;\n\n  @WorkflowInit\n  public MultiModelWorkflowImpl() {\n    // LinkedHashMap (not HashMap) so the keySet() rendering in the unknown-model error\n    // path is deterministic across replays. HashMap iteration order is unspecified and\n    // can differ between JVM versions or workers.\n    chatClients = new LinkedHashMap<>();\n\n    // The \"default\" and \"openai\" entries below both end up calling OpenAI in this sample —\n    // ChatModelConfig declares @Primary on openAiChatModel, so ActivityChatModel.forDefault()\n    // resolves to it. They're registered as two entries on purpose: \"default\" demonstrates\n    // the forDefault() / @Primary-resolution path (no bean name needed in workflow code),\n    // and \"openai\" demonstrates the forModel(name) explicit-lookup path. In a workflow that\n    // only uses one model, you'd typically pick one of these two patterns and stop there.\n    ActivityChatModel defaultModel = ActivityChatModel.forDefault();\n    chatClients.put(\n        \"default\",\n        TemporalChatClient.builder(defaultModel)\n            .defaultSystem(\"You are a helpful assistant. You are the DEFAULT model.\")\n            .build());\n\n    ActivityChatModel openAiModel = ActivityChatModel.forModel(\"openAiChatModel\");\n    chatClients.put(\n        \"openai\",\n        TemporalChatClient.builder(openAiModel)\n            .defaultSystem(\"You are a helpful assistant powered by OpenAI. Keep answers concise.\")\n            .build());\n\n    // Create a chat client using Anthropic Claude. The per-model ActivityOptions (longer\n    // start-to-close + schedule-to-close caps) live on the ChatModelActivityOptions bean in\n    // ChatModelConfig; forModel(name) consults that bean automatically. The workflow stays\n    // free of infrastructure wiring — ideal for static per-model config.\n    ActivityChatModel anthropicModel = ActivityChatModel.forModel(\"anthropicChatModel\");\n    chatClients.put(\n        \"anthropic\",\n        TemporalChatClient.builder(anthropicModel)\n            .defaultSystem(\n                \"You are a helpful assistant powered by Anthropic. \"\n                    + \"You excel at careful reasoning and nuanced responses.\")\n            .build());\n\n    // Create a chat client that turns on Anthropic's extended-thinking mode. This exercises\n    // the plugin's provider-specific ChatOptions pass-through end to end: the\n    // AnthropicChatOptions (with thinking=ENABLED + budget_tokens) is passed via\n    // .defaultOptions(...) on the ChatClient, crosses the activity boundary serialized as\n    // (class name, JSON), and is rehydrated by ChatModelActivityImpl before the prompt is\n    // sent to Claude. Required side effects for extended thinking: temperature must be 1.0\n    // and max_tokens must exceed budget_tokens.\n    // @@@SNIPSTART samples-java-spring-ai-provider-options\n    AnthropicChatOptions thinkingOptions =\n        AnthropicChatOptions.builder()\n            .thinking(AnthropicApi.ThinkingType.ENABLED, 1024)\n            .temperature(1.0)\n            .maxTokens(4096)\n            .build();\n    chatClients.put(\n        \"think\",\n        TemporalChatClient.builder(anthropicModel)\n            .defaultSystem(\n                \"You are a helpful assistant powered by Anthropic with extended thinking. \"\n                    + \"Use the thinking budget to reason carefully, then give a crisp answer \"\n                    + \"that reflects the reasoning you did.\")\n            .defaultOptions(thinkingOptions)\n            .build());\n    // @@@SNIPEND\n  }\n\n  @Override\n  public String run() {\n    // Wait until the chat is ended\n    Workflow.await(() -> ended);\n    return \"Chat ended after \" + messageCount + \" messages.\";\n  }\n\n  @Override\n  public void chat(String modelName, String message) {\n    messageCount++;\n\n    ChatClient client = chatClients.get(modelName);\n    if (client == null) {\n      lastResponse = \"Unknown model: \" + modelName + \". Available: \" + chatClients.keySet();\n      return;\n    }\n\n    lastResponse = client.prompt().user(message).call().content();\n  }\n\n  @Override\n  public String getLastResponse() {\n    return lastResponse;\n  }\n\n  @Override\n  public void end() {\n    ended = true;\n  }\n}\n"
  },
  {
    "path": "springai/multimodel/src/main/resources/application.yaml",
    "content": "spring:\n  main:\n    banner-mode: off\n    web-application-type: none\n  autoconfigure:\n    exclude:\n      - org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration\n      - org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration\n  ai:\n    openai:\n      api-key: ${OPENAI_API_KEY}\n    anthropic:\n      api-key: ${ANTHROPIC_API_KEY}\n    # Note: The actual models are configured in ChatModelConfig.java\n\n  temporal:\n    connection:\n      target: localhost:7233\n    workers:\n      - task-queue: multi-model-queue\n        workflow-classes:\n          - io.temporal.samples.springai.multimodel.MultiModelWorkflowImpl\n\nlogging:\n  level:\n    io.temporal.springai: DEBUG\n"
  },
  {
    "path": "springai/rag/build.gradle",
    "content": "apply from: \"$rootDir/gradle/springai.gradle\"\n\ndependencies {\n    implementation 'org.springframework.ai:spring-ai-starter-model-openai'\n    implementation 'org.springframework.ai:spring-ai-rag'\n}\n"
  },
  {
    "path": "springai/rag/src/main/java/io/temporal/samples/springai/rag/RagApplication.java",
    "content": "package io.temporal.samples.springai.rag;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport java.util.Scanner;\nimport java.util.UUID;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.CommandLineRunner;\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n/**\n * Example application demonstrating RAG with a Spring AI VectorStore.\n *\n * <p>This application shows how to use the plugin's VectorStoreActivity to build a durable\n * knowledge base within Temporal workflows. Embeddings are produced by whichever {@code\n * EmbeddingModel} the configured Spring AI {@code VectorStore} uses internally — this sample does\n * not invoke {@code EmbeddingModelActivity} directly.\n *\n * <h2>Usage</h2>\n *\n * <pre>\n * Commands:\n *   add &lt;id&gt; &lt;content&gt;  - Add a document to the knowledge base\n *   ask &lt;question&gt;      - Ask a question (uses RAG)\n *   search &lt;query&gt;      - Search for similar documents\n *   count               - Show document count\n *   quit                - End the session\n * </pre>\n *\n * <h2>Prerequisites</h2>\n *\n * <ol>\n *   <li>Start a Temporal dev server: {@code temporal server start-dev}\n *   <li>Set OPENAI_API_KEY environment variable\n *   <li>Run: {@code ./gradlew :springai:rag:bootRun}\n * </ol>\n */\n@SpringBootApplication\npublic class RagApplication implements CommandLineRunner {\n\n  private static final String TASK_QUEUE = \"rag-example-queue\";\n\n  @Autowired private WorkflowClient workflowClient;\n\n  public static void main(String[] args) {\n    SpringApplication.run(RagApplication.class, args);\n  }\n\n  @Override\n  public void run(String... args) throws Exception {\n    // Start a new workflow\n    String workflowId = \"rag-example-\" + UUID.randomUUID().toString().substring(0, 8);\n    RagWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            RagWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(TASK_QUEUE)\n                .setWorkflowId(workflowId)\n                .build());\n\n    // Start the workflow asynchronously\n    WorkflowClient.start(workflow::run);\n\n    System.out.println(\"\\n=== RAG (Retrieval-Augmented Generation) Demo ===\");\n    System.out.println(\"Workflow ID: \" + workflowId);\n    System.out.println(\"\\nThis demo uses VectorStoreActivity to build a durable knowledge\");\n    System.out.println(\"base with semantic search (embeddings are handled by the configured\");\n    System.out.println(\"Spring AI VectorStore).\");\n    System.out.println(\"\\nCommands:\");\n    System.out.println(\"  add <id> <content>  - Add a document\");\n    System.out.println(\"  ask <question>      - Ask a question (RAG)\");\n    System.out.println(\"  search <query>      - Search documents\");\n    System.out.println(\"  count               - Show document count\");\n    System.out.println(\"  quit                - End session\");\n    System.out.println(\"\\nTry adding some documents first, then ask questions about them!\");\n    System.out.println();\n\n    // Get a workflow stub for sending signals/queries\n    RagWorkflow workflowStub = workflowClient.newWorkflowStub(RagWorkflow.class, workflowId);\n\n    Scanner scanner = new Scanner(System.in, java.nio.charset.StandardCharsets.UTF_8);\n    while (true) {\n      System.out.print(\"> \");\n      String input = scanner.nextLine().trim();\n\n      if (input.equalsIgnoreCase(\"quit\")) {\n        workflowStub.end();\n        System.out.println(\"Session ended. Goodbye!\");\n        break;\n      }\n\n      if (input.equalsIgnoreCase(\"count\")) {\n        System.out.println(\"Documents in knowledge base: \" + workflowStub.getDocumentCount());\n        continue;\n      }\n\n      if (input.equals(\"add\") || input.startsWith(\"add \")) {\n        String rest = input.length() > 3 ? input.substring(4).trim() : \"\";\n        int spaceIndex = rest.indexOf(' ');\n        if (spaceIndex == -1) {\n          System.out.println(\"Usage: add <id> <content>\");\n          continue;\n        }\n        String id = rest.substring(0, spaceIndex);\n        String content = rest.substring(spaceIndex + 1).trim();\n\n        // Capture current response BEFORE sending so waitForResponse can detect when it changes.\n        String previousResponse = workflowStub.getLastResponse();\n        System.out.println(\"[Adding document...]\");\n        workflowStub.addDocument(id, content);\n        waitForResponse(workflowStub, previousResponse);\n        continue;\n      }\n\n      if (input.equals(\"ask\") || input.startsWith(\"ask \")) {\n        String question = input.length() > 3 ? input.substring(4).trim() : \"\";\n        if (question.isEmpty()) {\n          System.out.println(\"Usage: ask <question>\");\n          continue;\n        }\n\n        String previousResponse = workflowStub.getLastResponse();\n        System.out.println(\"[Searching and generating answer...]\");\n        workflowStub.ask(question);\n        waitForResponse(workflowStub, previousResponse);\n        continue;\n      }\n\n      if (input.equals(\"search\") || input.startsWith(\"search \")) {\n        String query = input.length() > 6 ? input.substring(7).trim() : \"\";\n        if (query.isEmpty()) {\n          System.out.println(\"Usage: search <query>\");\n          continue;\n        }\n\n        String previousResponse = workflowStub.getLastResponse();\n        System.out.println(\"[Searching...]\");\n        workflowStub.search(query, 5);\n        waitForResponse(workflowStub, previousResponse);\n        continue;\n      }\n\n      if (!input.isEmpty()) {\n        System.out.println(\"Unknown command. Use: add, ask, search, count, or quit\");\n      }\n    }\n  }\n\n  private void waitForResponse(RagWorkflow workflowStub, String previousResponse)\n      throws InterruptedException {\n    for (int i = 0; i < 600; i++) { // Wait up to 60 seconds\n      Thread.sleep(100);\n      String response = workflowStub.getLastResponse();\n      if (!response.equals(previousResponse)) {\n        System.out.println(\"\\n\" + response + \"\\n\");\n        return;\n      }\n    }\n    System.out.println(\"[Timeout waiting for response]\");\n  }\n}\n"
  },
  {
    "path": "springai/rag/src/main/java/io/temporal/samples/springai/rag/RagWorkflow.java",
    "content": "package io.temporal.samples.springai.rag;\n\nimport io.temporal.workflow.QueryMethod;\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n/**\n * Workflow interface demonstrating RAG (Retrieval-Augmented Generation).\n *\n * <p>This workflow shows how to use VectorStoreActivity and EmbeddingModelActivity to build a\n * durable knowledge base that can be queried with natural language.\n */\n@WorkflowInterface\npublic interface RagWorkflow {\n\n  /**\n   * Runs the workflow until ended.\n   *\n   * @return summary of the session\n   */\n  @WorkflowMethod\n  String run();\n\n  /**\n   * Adds a document to the knowledge base.\n   *\n   * @param id unique identifier for the document\n   * @param content the document content\n   */\n  @SignalMethod\n  void addDocument(String id, String content);\n\n  /**\n   * Asks a question using RAG - retrieves relevant documents and generates an answer.\n   *\n   * @param question the question to answer\n   */\n  @SignalMethod\n  void ask(String question);\n\n  /**\n   * Searches for similar documents without generating an answer.\n   *\n   * @param query the search query\n   * @param topK number of results to return\n   */\n  @SignalMethod\n  void search(String query, int topK);\n\n  /**\n   * Gets the last response from the AI or search.\n   *\n   * @return the last response\n   */\n  @QueryMethod\n  String getLastResponse();\n\n  /**\n   * Gets the current document count.\n   *\n   * @return number of documents in the knowledge base\n   */\n  @QueryMethod\n  int getDocumentCount();\n\n  /** Ends the session. */\n  @SignalMethod\n  void end();\n}\n"
  },
  {
    "path": "springai/rag/src/main/java/io/temporal/samples/springai/rag/RagWorkflowImpl.java",
    "content": "package io.temporal.samples.springai.rag;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.common.RetryOptions;\nimport io.temporal.springai.activity.VectorStoreActivity;\nimport io.temporal.springai.chat.TemporalChatClient;\nimport io.temporal.springai.model.ActivityChatModel;\nimport io.temporal.springai.model.VectorStoreTypes;\nimport io.temporal.workflow.Workflow;\nimport io.temporal.workflow.WorkflowInit;\nimport java.time.Duration;\nimport java.util.List;\nimport java.util.stream.Collectors;\nimport org.springframework.ai.chat.client.ChatClient;\n\n/**\n * Implementation of the RAG workflow.\n *\n * <p>This demonstrates:\n *\n * <ul>\n *   <li>Using {@link VectorStoreActivity} to store and search documents (the configured {@code\n *       VectorStore} handles embedding internally)\n *   <li>Combining vector search with chat for RAG\n * </ul>\n *\n * <p>All operations are durable Temporal activities - if the worker restarts, the workflow will\n * continue from where it left off.\n */\npublic class RagWorkflowImpl implements RagWorkflow {\n\n  private final VectorStoreActivity vectorStore;\n  private final ChatClient chatClient;\n\n  private String lastResponse = \"\";\n  private int documentCount = 0;\n  private boolean ended = false;\n\n  @WorkflowInit\n  public RagWorkflowImpl() {\n    // Create activity stubs with appropriate timeouts\n    ActivityOptions activityOptions =\n        ActivityOptions.newBuilder()\n            .setStartToCloseTimeout(Duration.ofMinutes(2))\n            .setRetryOptions(RetryOptions.newBuilder().setMaximumAttempts(3).build())\n            .build();\n\n    this.vectorStore = Workflow.newActivityStub(VectorStoreActivity.class, activityOptions);\n\n    // Create the chat client\n    ActivityChatModel chatModel = ActivityChatModel.forDefault();\n    this.chatClient =\n        TemporalChatClient.builder(chatModel)\n            .defaultSystem(\n                \"\"\"\n                You are a helpful assistant that answers questions based on the provided context.\n\n                When answering:\n                - Use only the information from the context provided\n                - If the context doesn't contain relevant information, say so\n                - Be concise and direct\n                \"\"\")\n            .build();\n  }\n\n  @Override\n  public String run() {\n    Workflow.await(() -> ended);\n    return \"Session ended. Processed \" + documentCount + \" documents.\";\n  }\n\n  @Override\n  public void addDocument(String id, String content) {\n    // Create a document and add it to the vector store\n    // The vector store will use the embedding model to generate embeddings\n    VectorStoreTypes.Document doc = new VectorStoreTypes.Document(id, content);\n    vectorStore.addDocuments(new VectorStoreTypes.AddDocumentsInput(List.of(doc)));\n\n    documentCount++;\n    lastResponse =\n        \"Added document '\" + id + \"' to knowledge base. Total documents: \" + documentCount;\n  }\n\n  @Override\n  public void ask(String question) {\n    // Step 1: Search for relevant documents\n    VectorStoreTypes.SearchOutput searchResults =\n        vectorStore.similaritySearch(new VectorStoreTypes.SearchInput(question, 3));\n\n    if (searchResults.documents().isEmpty()) {\n      lastResponse = \"No relevant documents found in the knowledge base.\";\n      return;\n    }\n\n    // Step 2: Build context from search results\n    String context =\n        searchResults.documents().stream()\n            .map(result -> result.document().text())\n            .collect(Collectors.joining(\"\\n\\n---\\n\\n\"));\n\n    // Step 3: Generate answer using the context\n    lastResponse =\n        chatClient\n            .prompt()\n            .user(\n                u ->\n                    u.text(\n                            \"\"\"\n                            Context:\n                            {context}\n\n                            Question: {question}\n\n                            Answer based on the context above:\n                            \"\"\")\n                        .param(\"context\", context)\n                        .param(\"question\", question))\n            .call()\n            .content();\n  }\n\n  @Override\n  public void search(String query, int topK) {\n    VectorStoreTypes.SearchOutput searchResults =\n        vectorStore.similaritySearch(new VectorStoreTypes.SearchInput(query, topK));\n\n    if (searchResults.documents().isEmpty()) {\n      lastResponse = \"No matching documents found.\";\n      return;\n    }\n\n    StringBuilder sb = new StringBuilder(\"Search results:\\n\\n\");\n    for (int i = 0; i < searchResults.documents().size(); i++) {\n      VectorStoreTypes.SearchResult result = searchResults.documents().get(i);\n      sb.append(\n          String.format(\n              \"%d. [Score: %.3f] %s\\n   %s\\n\\n\",\n              i + 1,\n              result.score(),\n              result.document().id(),\n              truncate(result.document().text(), 100)));\n    }\n    lastResponse = sb.toString();\n  }\n\n  @Override\n  public String getLastResponse() {\n    return lastResponse;\n  }\n\n  @Override\n  public int getDocumentCount() {\n    return documentCount;\n  }\n\n  @Override\n  public void end() {\n    ended = true;\n  }\n\n  private String truncate(String text, int maxLength) {\n    if (text.length() <= maxLength) {\n      return text;\n    }\n    return text.substring(0, maxLength) + \"...\";\n  }\n}\n"
  },
  {
    "path": "springai/rag/src/main/java/io/temporal/samples/springai/rag/VectorStoreConfig.java",
    "content": "package io.temporal.samples.springai.rag;\n\nimport org.springframework.ai.embedding.EmbeddingModel;\nimport org.springframework.ai.vectorstore.SimpleVectorStore;\nimport org.springframework.ai.vectorstore.VectorStore;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n/**\n * Configuration for the vector store.\n *\n * <p>This example uses Spring AI's SimpleVectorStore, an in-memory vector store that's perfect for\n * demos and testing. In production, you'd use a real vector database like Pinecone, Weaviate,\n * Milvus, or pgvector.\n */\n@Configuration\npublic class VectorStoreConfig {\n\n  /**\n   * Creates an in-memory vector store using the provided embedding model.\n   *\n   * <p>The SimpleVectorStore stores vectors in memory and uses the embedding model to convert text\n   * to vectors when documents are added.\n   *\n   * @param embeddingModel the embedding model to use for vectorization\n   * @return the configured vector store\n   */\n  @Bean\n  public VectorStore vectorStore(EmbeddingModel embeddingModel) {\n    return SimpleVectorStore.builder(embeddingModel).build();\n  }\n}\n"
  },
  {
    "path": "springai/rag/src/main/resources/application.yaml",
    "content": "spring:\n  main:\n    banner-mode: off\n    web-application-type: none\n  ai:\n    openai:\n      api-key: ${OPENAI_API_KEY}\n      chat:\n        options:\n          model: gpt-4o-mini\n      embedding:\n        options:\n          model: text-embedding-3-small\n\n  temporal:\n    connection:\n      target: localhost:7233\n    workers:\n      - task-queue: rag-example-queue\n        workflow-classes:\n          - io.temporal.samples.springai.rag.RagWorkflowImpl\n\nlogging:\n  level:\n    io.temporal.springai: DEBUG\n"
  },
  {
    "path": "springboot/build.gradle",
    "content": "apply plugin: 'org.springframework.boot'\n\ndependencies {\n    implementation \"org.springframework.boot:spring-boot-starter-web\"\n    implementation \"org.springframework.boot:spring-boot-starter-thymeleaf\"\n    implementation \"org.springframework.boot:spring-boot-starter-actuator\"\n    implementation \"org.springframework.boot:spring-boot-starter-data-jpa\"\n    implementation \"org.springframework.kafka:spring-kafka\"\n    // we set this as impl depends to use embedded kafka in samples not just tests\n    implementation \"org.springframework.kafka:spring-kafka-test\"\n    implementation \"io.temporal:temporal-spring-boot-starter:$javaSDKVersion\"\n\n    // Environment configuration\n    implementation \"io.temporal:temporal-envconfig:$javaSDKVersion\"\n    implementation \"org.apache.camel.springboot:camel-spring-boot-starter:$camelVersion\"\n    implementation \"org.apache.camel.springboot:camel-servlet-starter:$camelVersion\"\n    runtimeOnly \"io.micrometer:micrometer-registry-prometheus\"\n    runtimeOnly \"com.h2database:h2\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test\"\n    dependencies {\n        errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')\n        errorprone('com.google.errorprone:error_prone_core:2.28.0')\n    }\n}\n\nbootJar {\n    enabled = false\n}\n\njar {\n    enabled = true\n}"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/SamplesController.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.grpc.StatusRuntimeException;\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.client.WorkflowUpdateException;\nimport io.temporal.samples.springboot.customize.CustomizeWorkflow;\nimport io.temporal.samples.springboot.hello.HelloWorkflow;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.samples.springboot.kafka.MessageWorkflow;\nimport io.temporal.samples.springboot.update.PurchaseWorkflow;\nimport io.temporal.samples.springboot.update.model.ProductRepository;\nimport io.temporal.samples.springboot.update.model.Purchase;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.*;\n\n@Controller\npublic class SamplesController {\n\n  @Autowired WorkflowClient client;\n\n  @Autowired ProductRepository productRepository;\n\n  @GetMapping(\"/hello\")\n  public String hello(Model model) {\n    model.addAttribute(\"sample\", \"Say Hello\");\n    return \"hello\";\n  }\n\n  @PostMapping(\n      value = \"/hello\",\n      consumes = {MediaType.APPLICATION_JSON_VALUE},\n      produces = {MediaType.TEXT_HTML_VALUE})\n  ResponseEntity helloSample(@RequestBody Person person) {\n    HelloWorkflow workflow =\n        client.newWorkflowStub(\n            HelloWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"HelloSampleTaskQueue\")\n                .setWorkflowId(\"HelloSample\")\n                .build());\n\n    // bypass thymeleaf, don't return template name just result\n    return new ResponseEntity<>(\"\\\"\" + workflow.sayHello(person) + \"\\\"\", HttpStatus.OK);\n  }\n\n  @GetMapping(\"/metrics\")\n  public String metrics(Model model) {\n    model.addAttribute(\"sample\", \"SDK Metrics\");\n    return \"metrics\";\n  }\n\n  @GetMapping(\"/update\")\n  public String update(Model model) {\n    model.addAttribute(\"sample\", \"Synchronous Update\");\n    model.addAttribute(\"products\", productRepository.findAll());\n    return \"update\";\n  }\n\n  @GetMapping(\"/update/inventory\")\n  public String updateInventory(Model model) {\n    model.addAttribute(\"products\", productRepository.findAll());\n    return \"update :: inventory\";\n  }\n\n  @PostMapping(\n      value = \"/update/purchase\",\n      consumes = {MediaType.APPLICATION_JSON_VALUE},\n      produces = {MediaType.TEXT_HTML_VALUE})\n  ResponseEntity purchase(@RequestBody Purchase purchase) {\n    PurchaseWorkflow workflow =\n        client.newWorkflowStub(\n            PurchaseWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"UpdateSampleTaskQueue\")\n                .setWorkflowId(\"NewPurchase\")\n                .build());\n    WorkflowClient.start(workflow::start);\n\n    // send update\n    try {\n      boolean isValidPurchase = workflow.makePurchase(purchase);\n      // for sample send exit to workflow exec and wait till it completes\n      workflow.exit();\n      WorkflowStub.fromTyped(workflow).getResult(Void.class);\n      if (!isValidPurchase) {\n        return new ResponseEntity<>(\"\\\"Invalid purchase\\\"\", HttpStatus.NOT_FOUND);\n      }\n      return new ResponseEntity<>(\"\\\"\" + \"Purchase successful\" + \"\\\"\", HttpStatus.OK);\n    } catch (WorkflowUpdateException | StatusRuntimeException e) {\n      // for sample send exit to workflow exec and wait till it completes\n      workflow.exit();\n      WorkflowStub.fromTyped(workflow).getResult(Void.class);\n\n      String message = e.getMessage();\n      if (e instanceof WorkflowUpdateException) {\n        message = e.getCause().getMessage();\n      }\n\n      return new ResponseEntity<>(\"\\\"\" + message + \"\\\"\", HttpStatus.NOT_FOUND);\n    }\n  }\n\n  @GetMapping(\"/kafka\")\n  public String kafka(Model model) {\n    model.addAttribute(\"sample\", \"Kafka Request / Reply\");\n    return \"kafka\";\n  }\n\n  @PostMapping(\n      value = \"/kafka\",\n      consumes = {MediaType.TEXT_PLAIN_VALUE},\n      produces = {MediaType.TEXT_HTML_VALUE})\n  ResponseEntity sendToKafka(@RequestBody String message) {\n    MessageWorkflow workflow =\n        client.newWorkflowStub(\n            MessageWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"KafkaSampleTaskQueue\")\n                .setWorkflowId(\"MessageSample\")\n                .build());\n\n    WorkflowClient.start(workflow::start);\n    workflow.update(message);\n\n    // wait till exec completes\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n    // bypass thymeleaf, don't return template name just result\n    return new ResponseEntity<>(\"\\\" Message workflow completed\\\"\", HttpStatus.OK);\n  }\n\n  @GetMapping(\"/customize\")\n  public String customize(Model model) {\n    model.addAttribute(\"sample\", \"Customizing Options\");\n    return \"customize\";\n  }\n\n  @PostMapping(\n      value = \"/customize\",\n      consumes = {MediaType.APPLICATION_JSON_VALUE},\n      produces = {MediaType.TEXT_HTML_VALUE})\n  ResponseEntity customizeSample() {\n    CustomizeWorkflow workflow =\n        client.newWorkflowStub(\n            CustomizeWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"CustomizeTaskQueue\")\n                .setWorkflowId(\"CustomizeSample\")\n                .build());\n\n    // bypass thymeleaf, don't return template name just result\n    return new ResponseEntity<>(\"\\\"\" + workflow.execute() + \"\\\"\", HttpStatus.OK);\n  }\n\n  @GetMapping(\"/camel\")\n  public String camel(Model model) {\n    model.addAttribute(\"sample\", \"Camel Route\");\n    return \"camel\";\n  }\n\n  @GetMapping(\"/customendpoint\")\n  public String customEndpoint(Model model) {\n    model.addAttribute(\"sample\", \"Custom Actuator Worker Info Endpoint\");\n    return \"actuator\";\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/TemporalSpringbootSamplesApplication.java",
    "content": "package io.temporal.samples.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class TemporalSpringbootSamplesApplication {\n  public static void main(String[] args) {\n    SpringApplication.run(TemporalSpringbootSamplesApplication.class, args);\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/actuator/README.md",
    "content": "# SpringBoot Actuator Worker Info Endpoint - Sample\n\n1. Start SpringBoot from main samples repo directory:\n\n       ./gradlew bootRun\n\n2. In your browser navigate to:\n\n       http://localhost:3030/actuator/temporalworkerinfo\n\nThis sample shows how to create a custom Actuator Endpoint that\ndisplays registered workflow and activity implementations per task queue.\nThis information comes from actually registered workers done by autoconfig module."
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/actuator/WorkerActuatorEndpoint.java",
    "content": "package io.temporal.samples.springboot.actuator;\n\nimport io.temporal.common.metadata.*;\nimport io.temporal.spring.boot.autoconfigure.template.WorkersTemplate;\nimport java.lang.reflect.Method;\nimport java.util.Map;\nimport java.util.stream.Collectors;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Qualifier;\nimport org.springframework.boot.actuate.endpoint.annotation.Endpoint;\nimport org.springframework.boot.actuate.endpoint.annotation.ReadOperation;\nimport org.springframework.stereotype.Component;\n\n@Component\n@Endpoint(id = \"temporalworkerinfo\")\npublic class WorkerActuatorEndpoint {\n  @Autowired\n  @Qualifier(\"temporalWorkersTemplate\")\n  private WorkersTemplate workersTemplate;\n\n  @ReadOperation\n  public String workerInfo() {\n    StringBuilder sb = new StringBuilder();\n    Map<String, WorkersTemplate.RegisteredInfo> registeredInfo =\n        workersTemplate.getRegisteredInfo();\n    sb.append(\"Worker Info:\");\n    registeredInfo.forEach(\n        (taskQueue, info) -> {\n          sb.append(\"\\n\\n\\tTask Queue: \").append(taskQueue);\n          info.getRegisteredWorkflowInfo()\n              .forEach(\n                  (workflowInfo) -> {\n                    sb.append(\"\\n\\t\\t Workflow Interface: \").append(workflowInfo.getClassName());\n                    POJOWorkflowImplMetadata metadata = workflowInfo.getMetadata();\n                    sb.append(\"\\n\\t\\t\\t Workflow Methods: \");\n                    sb.append(\n                        metadata.getWorkflowMethods().stream()\n                            .map(POJOWorkflowMethodMetadata::getWorkflowMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\", \")));\n                    sb.append(\"\\n\\t\\t\\t Query Methods: \");\n                    sb.append(\n                        metadata.getQueryMethods().stream()\n                            .map(POJOWorkflowMethodMetadata::getWorkflowMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\", \")));\n                    sb.append(\"\\n\\t\\t\\t Signal Methods: \");\n                    sb.append(\n                        metadata.getSignalMethods().stream()\n                            .map(POJOWorkflowMethodMetadata::getWorkflowMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\", \")));\n                    sb.append(\"\\n\\t\\t\\t Update Methods: \");\n                    sb.append(\n                        metadata.getUpdateMethods().stream()\n                            .map(POJOWorkflowMethodMetadata::getWorkflowMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\",\")));\n                    sb.append(\"\\n\\t\\t\\t Update Validator Methods: \");\n                    sb.append(\n                        metadata.getUpdateValidatorMethods().stream()\n                            .map(POJOWorkflowMethodMetadata::getWorkflowMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\", \")));\n                  });\n          info.getRegisteredActivityInfo()\n              .forEach(\n                  (activityInfo) -> {\n                    sb.append(\"\\n\\t\\t Activity Impl: \").append(activityInfo.getClassName());\n                    POJOActivityImplMetadata metadata = activityInfo.getMetadata();\n                    sb.append(\"\\n\\t\\t\\t Activity Interfaces: \");\n                    sb.append(\n                        metadata.getActivityInterfaces().stream()\n                            .map(POJOActivityInterfaceMetadata::getInterfaceClass)\n                            .map(Class::getName)\n                            .collect(Collectors.joining(\",\")));\n                    sb.append(\"\\n\\t\\t\\t Activity Methods: \");\n                    sb.append(\n                        metadata.getActivityMethods().stream()\n                            .map(POJOActivityMethodMetadata::getMethod)\n                            .map(Method::getName)\n                            .collect(Collectors.joining(\", \")));\n                  });\n        });\n\n    return sb.toString();\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/CamelConfig.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport org.apache.camel.CamelContext;\nimport org.apache.camel.ConsumerTemplate;\nimport org.apache.camel.ProducerTemplate;\nimport org.apache.camel.component.servlet.CamelHttpTransportServlet;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.servlet.ServletRegistrationBean;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\n\n@Configuration()\n@Profile(\"!test\")\npublic class CamelConfig {\n  @Autowired private CamelContext camelContext;\n\n  @Bean\n  ServletRegistrationBean servletRegistrationBean() {\n    String contextPath = \"/temporalapp\";\n    ServletRegistrationBean servlet =\n        new ServletRegistrationBean(new CamelHttpTransportServlet(), contextPath + \"/*\");\n    servlet.setName(\"CamelServlet\");\n    return servlet;\n  }\n\n  @Bean\n  ProducerTemplate producerTemplate() {\n    return camelContext.createProducerTemplate();\n  }\n\n  @Bean\n  ConsumerTemplate consumerTemplate() {\n    return camelContext.createConsumerTemplate();\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/CamelResource.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport java.util.List;\nimport org.apache.camel.ProducerTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.ResponseBody;\nimport org.springframework.web.bind.annotation.RestController;\n\n@RestController\npublic class CamelResource {\n  @Autowired private ProducerTemplate producerTemplate;\n\n  @GetMapping(\"/orders\")\n  @ResponseBody\n  public List<OfficeOrder> getProductsByCategory() {\n    producerTemplate.start();\n    List<OfficeOrder> orders = producerTemplate.requestBody(\"direct:getOrders\", null, List.class);\n    producerTemplate.stop();\n    return orders;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/CamelRoutes.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport org.apache.camel.builder.RouteBuilder;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class CamelRoutes extends RouteBuilder {\n\n  @Autowired private WorkflowClient workflowClient;\n  @Autowired OrderRepository repository;\n\n  @Override\n  public void configure() {\n    from(\"direct:getOrders\")\n        .routeId(\"direct-getOrders\")\n        .tracing()\n        .process(\n            exchange -> {\n              OrderWorkflow workflow =\n                  workflowClient.newWorkflowStub(\n                      OrderWorkflow.class,\n                      WorkflowOptions.newBuilder()\n                          .setWorkflowId(\"CamelSampleWorkflow\")\n                          .setTaskQueue(\"CamelSampleTaskQueue\")\n                          .build());\n              exchange.getIn().setBody(workflow.start());\n            })\n        .end();\n\n    from(\"direct:findAllOrders\")\n        .routeId(\"direct-findAllOrders\")\n        .process(\n            exchange -> {\n              exchange.getIn().setBody(repository.findAll());\n            })\n        .end();\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OfficeOrder.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport javax.persistence.*;\n\n@Entity\n@Table(name = \"officeorder\")\npublic class OfficeOrder {\n  @Id\n  @GeneratedValue(strategy = GenerationType.IDENTITY)\n  private Integer id;\n\n  @Column(nullable = false)\n  private String number;\n\n  @Column(nullable = false)\n  private String desc;\n\n  @Column(nullable = false)\n  private String date;\n\n  @Column(nullable = false)\n  private double price;\n\n  public OfficeOrder() {}\n\n  public OfficeOrder(Integer id, String number, String desc, String date, double price) {\n    this.id = id;\n    this.number = number;\n    this.desc = desc;\n    this.date = date;\n    this.price = price;\n  }\n\n  public String getDesc() {\n    return desc;\n  }\n\n  public void setDesc(String desc) {\n    this.desc = desc;\n  }\n\n  public String getNumber() {\n    return number;\n  }\n\n  public Integer getId() {\n    return id;\n  }\n\n  public void setId(Integer id) {\n    this.id = id;\n  }\n\n  public void setNumber(String number) {\n    this.number = number;\n  }\n\n  public String getDate() {\n    return date;\n  }\n\n  public void setDate(String date) {\n    this.date = date;\n  }\n\n  public double getPrice() {\n    return price;\n  }\n\n  public void setPrice(double price) {\n    this.price = price;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OrderActivity.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport io.temporal.activity.ActivityInterface;\nimport java.util.List;\n\n@ActivityInterface\npublic interface OrderActivity {\n  List<OfficeOrder> getOrders();\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OrderActivityImpl.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport io.temporal.spring.boot.ActivityImpl;\nimport java.util.List;\nimport org.apache.camel.ProducerTemplate;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"CamelSampleTaskQueue\")\npublic class OrderActivityImpl implements OrderActivity {\n\n  @Autowired private ProducerTemplate producerTemplate;\n\n  @Override\n  public List<OfficeOrder> getOrders() {\n    producerTemplate.start();\n    List<OfficeOrder> orders =\n        producerTemplate.requestBody(\"direct:findAllOrders\", null, List.class);\n    producerTemplate.stop();\n    return orders;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OrderRepository.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface OrderRepository extends JpaRepository<OfficeOrder, Integer> {}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OrderWorkflow.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\nimport java.util.List;\n\n@WorkflowInterface\npublic interface OrderWorkflow {\n  @WorkflowMethod\n  public List<OfficeOrder> start();\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/OrderWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.camel;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport java.util.List;\n\n@WorkflowImpl(taskQueues = \"CamelSampleTaskQueue\")\npublic class OrderWorkflowImpl implements OrderWorkflow {\n\n  private OrderActivity activity =\n      Workflow.newActivityStub(\n          OrderActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public List<OfficeOrder> start() {\n    return activity.getOrders();\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/camel/README.md",
    "content": "# SpringBoot Camel Sample\n\n1. Start SpringBoot from main samples repo directory:\n\n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n\n       http://localhost:3030/orders\n\nThis sample starts an Apache Camel route which starts our orders Workflow.\nThe workflow starts an activity which starts Camel route to get all orders JPA."
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeActivity.java",
    "content": "package io.temporal.samples.springboot.customize;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface CustomizeActivity {\n  String run(String input);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeActivityImpl.java",
    "content": "package io.temporal.samples.springboot.customize;\n\nimport io.temporal.spring.boot.ActivityImpl;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"CustomizeTaskQueue\")\npublic class CustomizeActivityImpl implements CustomizeActivity {\n  @Override\n  public String run(String input) {\n    return \"Completed as \" + input + \" activity!\";\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeWorkflow.java",
    "content": "package io.temporal.samples.springboot.customize;\n\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface CustomizeWorkflow {\n  @WorkflowMethod\n  String execute();\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/CustomizeWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.customize;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.activity.LocalActivityOptions;\nimport io.temporal.failure.ActivityFailure;\nimport io.temporal.failure.TimeoutFailure;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\nimport org.slf4j.Logger;\n\n/**\n * In our custom config we have set that worker polling on CustomizeTaskQueue to be a \"local\n * activity worker\", meaning it would not poll for activity tasks. For this sample we will try to\n * start an activity as \"normal\" activity which should time out, then invoke it again as local which\n * should be successful.\n *\n * @see io.temporal.samples.springboot.customize.TemporalOptionsConfig\n */\n@WorkflowImpl(taskQueues = \"CustomizeTaskQueue\")\npublic class CustomizeWorkflowImpl implements CustomizeWorkflow {\n  private CustomizeActivity asNormalActivity =\n      Workflow.newActivityStub(\n          CustomizeActivity.class,\n          ActivityOptions.newBuilder()\n              .setStartToCloseTimeout(Duration.ofSeconds(2))\n              .setScheduleToCloseTimeout(Duration.ofSeconds(4))\n              .build());\n\n  private CustomizeActivity asLocalActivity =\n      Workflow.newLocalActivityStub(\n          CustomizeActivity.class,\n          LocalActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n  private Logger logger = Workflow.getLogger(CustomizeActivity.class.getName());\n\n  @Override\n  public String execute() {\n    try {\n      return asNormalActivity.run(\"Normal\");\n    } catch (ActivityFailure e) {\n      // We should have TimeoutFailure as activity failure cause with StartToClose timeout type\n      TimeoutFailure tf = (TimeoutFailure) e.getCause();\n      logger.warn(\"asNormalActivity failed with timeout type: \" + tf.getTimeoutType());\n    }\n    return asLocalActivity.run(\"Local\");\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/README.md",
    "content": "# SpringBoot Customize Options Sample\n\nThis sample shows how to optimize default options such as\n* WorkflowServiceStubsOptions\n* WorkflowClientOption\n* WorkerFactoryOptions\n* WorkerOptions\n\nWorkerOptions can be optimized per worker/task queue.\n\nFor this sample we set our specific worker to be \"local activity worker\" via custom options meaning\nit would not poll for activity tasks. Click on \"Run Workflow\" button to start instance of\nour sample workflow. This workflow will try to invoke our activity as \"normal\"\nactivity which should time out on ScheduleToClose timeout, then we invoke this activity\nas local activity which should succeed.\n\n## How to run\n1. Start SpringBoot from main samples repo directory:\n\n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n\n       http://localhost:3030/customize\n\n3. Press the \"Run Workflow\" button to start execution. You will see result show on page in 4 seconds"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/customize/TemporalOptionsConfig.java",
    "content": "package io.temporal.samples.springboot.customize;\n\nimport io.temporal.client.WorkflowClientOptions;\nimport io.temporal.serviceclient.WorkflowServiceStubsOptions;\nimport io.temporal.spring.boot.TemporalOptionsCustomizer;\nimport io.temporal.spring.boot.WorkerOptionsCustomizer;\nimport io.temporal.worker.WorkerFactoryOptions;\nimport io.temporal.worker.WorkerOptions;\nimport io.temporal.worker.WorkflowImplementationOptions;\nimport javax.annotation.Nonnull;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\n\n@Configuration\npublic class TemporalOptionsConfig {\n\n  // Worker specific options customization\n  @Bean\n  public WorkerOptionsCustomizer customWorkerOptions() {\n    return new WorkerOptionsCustomizer() {\n      @Nonnull\n      @Override\n      public WorkerOptions.Builder customize(\n          @Nonnull WorkerOptions.Builder optionsBuilder,\n          @Nonnull String workerName,\n          @Nonnull String taskQueue) {\n\n        // For CustomizeTaskQueue (also name of worker) we set worker\n        // to only handle workflow tasks and local activities\n        if (taskQueue.equals(\"CustomizeTaskQueue\")) {\n          optionsBuilder.setLocalActivityWorkerOnly(true);\n        }\n        return optionsBuilder;\n      }\n    };\n  }\n\n  // WorkflowServiceStubsOptions customization\n  @Bean\n  public TemporalOptionsCustomizer<WorkflowServiceStubsOptions.Builder>\n      customServiceStubsOptions() {\n    return new TemporalOptionsCustomizer<WorkflowServiceStubsOptions.Builder>() {\n      @Nonnull\n      @Override\n      public WorkflowServiceStubsOptions.Builder customize(\n          @Nonnull WorkflowServiceStubsOptions.Builder optionsBuilder) {\n        // set options on optionsBuilder as needed\n        // ...\n        return optionsBuilder;\n      }\n    };\n  }\n\n  // WorkflowClientOption customization\n  @Bean\n  public TemporalOptionsCustomizer<WorkflowClientOptions.Builder> customClientOptions() {\n    return new TemporalOptionsCustomizer<WorkflowClientOptions.Builder>() {\n      @Nonnull\n      @Override\n      public WorkflowClientOptions.Builder customize(\n          @Nonnull WorkflowClientOptions.Builder optionsBuilder) {\n        // set options on optionsBuilder as needed\n        // ...\n        return optionsBuilder;\n      }\n    };\n  }\n\n  // WorkerFactoryOptions customization\n  @Bean\n  public TemporalOptionsCustomizer<WorkerFactoryOptions.Builder> customWorkerFactoryOptions() {\n    return new TemporalOptionsCustomizer<WorkerFactoryOptions.Builder>() {\n      @Nonnull\n      @Override\n      public WorkerFactoryOptions.Builder customize(\n          @Nonnull WorkerFactoryOptions.Builder optionsBuilder) {\n        // set options on optionsBuilder as needed\n        // ...\n        return optionsBuilder;\n      }\n    };\n  }\n\n  // WorkflowImplementationOptions customization\n  @Bean\n  public TemporalOptionsCustomizer<WorkflowImplementationOptions.Builder>\n      customWorkflowImplementationOptions() {\n    return new TemporalOptionsCustomizer<>() {\n      @Nonnull\n      @Override\n      public WorkflowImplementationOptions.Builder customize(\n          @Nonnull WorkflowImplementationOptions.Builder optionsBuilder) {\n        // set options on optionsBuilder such as per-activity options\n        return optionsBuilder;\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/HelloActivity.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.samples.springboot.hello.model.Person;\n\n@ActivityInterface\npublic interface HelloActivity {\n  String hello(Person person);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/HelloActivityImpl.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.spring.boot.ActivityImpl;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"HelloSampleTaskQueue\")\npublic class HelloActivityImpl implements HelloActivity {\n  @Value(\"${samples.data.language}\")\n  private String language;\n\n  @Override\n  public String hello(Person person) {\n    String greeting = language.equals(\"spanish\") ? \"Hola \" : \"Hello \";\n    return greeting + person.getFirstName() + \" \" + person.getLastName() + \"!\";\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflow.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloWorkflow {\n  @WorkflowMethod\n  String sayHello(Person person);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n@WorkflowImpl(taskQueues = \"HelloSampleTaskQueue\")\npublic class HelloWorkflowImpl implements HelloWorkflow {\n\n  private HelloActivity activity =\n      Workflow.newActivityStub(\n          HelloActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String sayHello(Person person) {\n    return activity.hello(person);\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/README.md",
    "content": "# SpringBoot Hello Sample\n\n1. Start SpringBoot from main samples repo directory:\n   \n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n \n       http://localhost:3030/hello\n\nEnter in first and last name in the form then click on Run Workflow\nto start workflow execution. Page will be updated to show the workflow\nexecution results (the greeting).\n\nYou can try changing the language setting in [application.yaml](../../../../../../resources/application.yaml) file\nfrom \"english\" to \"spanish\" to get the greeting result in Spanish."
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/hello/model/Person.java",
    "content": "package io.temporal.samples.springboot.hello.model;\n\npublic class Person {\n  private String firstName;\n  private String lastName;\n\n  public Person() {}\n\n  public Person(String firstName, String lastName) {\n    this.firstName = firstName;\n    this.lastName = lastName;\n  }\n\n  public String getFirstName() {\n    return firstName;\n  }\n\n  public void setFirstName(String firstName) {\n    this.firstName = firstName;\n  }\n\n  public String getLastName() {\n    return lastName;\n  }\n\n  public void setLastName(String lastName) {\n    this.lastName = lastName;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/KafkaActivity.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport io.temporal.activity.ActivityInterface;\n\n@ActivityInterface\npublic interface KafkaActivity {\n  void sendMessage(String message);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/KafkaActivityImpl.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.spring.boot.ActivityImpl;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.kafka.core.KafkaTemplate;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"KafkaSampleTaskQueue\")\npublic class KafkaActivityImpl implements KafkaActivity {\n\n  // Setting required to false means we won't fail\n  // if a test does not have kafka enabled\n  @Autowired(required = false)\n  private KafkaTemplate<String, String> kafkaTemplate;\n\n  @Value(value = \"${samples.message.topic.name}\")\n  private String topicName;\n\n  @Override\n  public void sendMessage(String message) {\n    try {\n      kafkaTemplate.send(topicName, message).get();\n    } catch (Exception e) {\n      throw ApplicationFailure.newFailure(\n          \"Unable to send message.\", e.getClass().getName(), e.getMessage());\n    }\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/KafkaConfig.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport java.io.IOException;\nimport java.util.HashMap;\nimport java.util.Map;\nimport org.apache.kafka.clients.admin.AdminClientConfig;\nimport org.apache.kafka.clients.admin.NewTopic;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.context.annotation.Profile;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.kafka.core.KafkaAdmin;\nimport org.springframework.kafka.test.EmbeddedKafkaBroker;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\n@Configuration()\n@Profile(\"!test\")\npublic class KafkaConfig {\n  @Value(value = \"${spring.kafka.bootstrap-servers}\")\n  private String bootstrapAddress;\n\n  @Value(value = \"${samples.message.topic.name}\")\n  private String topicName;\n\n  @Autowired private MessageController messageController;\n\n  @Bean\n  EmbeddedKafkaBroker broker() {\n    return new EmbeddedKafkaBroker(1)\n        .kafkaPorts(9092)\n        .brokerListProperty(\"spring.kafka.bootstrap-servers\");\n  }\n\n  @Bean\n  public KafkaAdmin kafkaAdmin() {\n    Map<String, Object> configs = new HashMap<>();\n    configs.put(AdminClientConfig.BOOTSTRAP_SERVERS_CONFIG, bootstrapAddress);\n    return new KafkaAdmin(configs);\n  }\n\n  @Bean\n  public NewTopic samplesTopic() {\n    return new NewTopic(topicName, 1, (short) 1);\n  }\n\n  @KafkaListener(id = \"samples-topic\", topics = \"samples-topic\")\n  public void kafkaListener(String message) {\n    SseEmitter latestEm = messageController.getLatestEmitter();\n\n    try {\n      latestEm.send(message);\n    } catch (IOException e) {\n      latestEm.completeWithError(e);\n    }\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageController.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport java.util.ArrayList;\nimport java.util.List;\nimport org.springframework.web.bind.annotation.GetMapping;\nimport org.springframework.web.bind.annotation.RestController;\nimport org.springframework.web.servlet.mvc.method.annotation.SseEmitter;\n\n@RestController\npublic class MessageController {\n  private final List<SseEmitter> emitters = new ArrayList<>();\n\n  @GetMapping(\"/kafka-messages\")\n  public SseEmitter getKafkaMessages() {\n\n    SseEmitter emitter = new SseEmitter(60 * 1000L);\n    emitters.add(emitter);\n\n    emitter.onCompletion(() -> emitters.remove(emitter));\n\n    emitter.onTimeout(() -> emitters.remove(emitter));\n\n    return emitter;\n  }\n\n  public List<SseEmitter> getEmitters() {\n    return emitters;\n  }\n\n  public SseEmitter getLatestEmitter() {\n    return emitters.isEmpty() ? null : emitters.get(emitters.size() - 1);\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageWorkflow.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport io.temporal.workflow.SignalMethod;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface MessageWorkflow {\n\n  @WorkflowMethod\n  void start();\n\n  @SignalMethod\n  void update(String message);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/MessageWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.kafka;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n@WorkflowImpl(taskQueues = \"KafkaSampleTaskQueue\")\npublic class MessageWorkflowImpl implements MessageWorkflow {\n\n  private KafkaActivity activity =\n      Workflow.newActivityStub(\n          KafkaActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n  private String message = null;\n\n  @Override\n  public void start() {\n    Workflow.await(() -> message != null);\n    // simulate some steps / milestones\n    activity.sendMessage(\n        \"Starting execution: \" + Workflow.getInfo().getWorkflowId() + \" with message: \" + message);\n\n    activity.sendMessage(\"Step 1 done...\");\n    activity.sendMessage(\"Step 2 done...\");\n    activity.sendMessage(\"Step 3 done...\");\n\n    activity.sendMessage(\"Completing execution: \" + Workflow.getInfo().getWorkflowId());\n  }\n\n  @Override\n  public void update(String message) {\n    this.message = message;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/kafka/README.md",
    "content": "# SpringBoot Kafka Request / Reply Sample\n\n1. Start SpringBoot from main samples repo directory:\n\n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n\n       http://localhost:3030/kafka\n\n## Use Case\nWhen you start adopting Temporal in your existing applications it can \nvery often become a much better replacement to any queueing techs like Kafka.\nEven tho we can replace big parts of our unreliable architecture with Temporal\noften it's not a complete replacement and we still have services that produce \nmessages/events to Kafka topics and workflow results need to be pushed to Kafka in order\nto be consumed by these existing services.\nIn this sample we show how messages sent to Kafka topics can trigger workflow execution\nas well as how via activities we can produce messages to Kafka topics that can be consumed\nby other existing services you might have. \n\n## How to use\nEnter a message you want to set to Kafka topic. Message consumer when it receives it \nwill start a workflow execution and deliver message to it as signal. \nWorkflow execution performs some sample steps. For each step it executes an activity which uses\nKafka producer to send message to Kafka topic. \nIn the UI we listen on this kafka topic and you will see all messages produced by activities\nshow up dynamically as they are happening.\n\n## Note\nWe use embedded (in-memory) Kafka to make it easier to run this sample.\nYou should not use this in your applications outside of tests. "
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/metrics/README.md",
    "content": "# SpringBoot Metrics Sample\n\n1. Start SpringBoot from main samples repo directory:\n   \n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n \n       http://localhost:3030/metrics\n\nThis sample involves just SpringBoot and Actuator configurations which are \nincluded in the samples already. The page shows information on how to set up\nSDK metrics in your SpringBoot applications. "
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/ProductNotAvailableForAmountException.java",
    "content": "package io.temporal.samples.springboot.update;\n\npublic class ProductNotAvailableForAmountException extends Exception {\n  public ProductNotAvailableForAmountException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseActivities.java",
    "content": "package io.temporal.samples.springboot.update;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.samples.springboot.update.model.Purchase;\n\n@ActivityInterface\npublic interface PurchaseActivities {\n  boolean isProductInStockForPurchase(Purchase purchase);\n\n  boolean makePurchase(Purchase purchase);\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseActivitiesImpl.java",
    "content": "package io.temporal.samples.springboot.update;\n\nimport io.temporal.samples.springboot.update.model.Product;\nimport io.temporal.samples.springboot.update.model.ProductRepository;\nimport io.temporal.samples.springboot.update.model.Purchase;\nimport io.temporal.spring.boot.ActivityImpl;\nimport java.util.Optional;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"UpdateSampleTaskQueue\")\npublic class PurchaseActivitiesImpl implements PurchaseActivities {\n  @Autowired ProductRepository productRepository;\n\n  @Override\n  public boolean isProductInStockForPurchase(Purchase purchase) {\n    Product product = getProductFor(purchase);\n    return product != null && product.getStock() >= purchase.getAmount();\n  }\n\n  @Override\n  public boolean makePurchase(Purchase purchase) {\n    Product product = getProductFor(purchase);\n    if (product != null) {\n      product.setStock(product.getStock() - purchase.getAmount());\n      productRepository.save(product);\n      return true;\n    }\n    return false;\n  }\n\n  private Product getProductFor(Purchase purchase) {\n    Optional<Product> productOptional = productRepository.findById(purchase.getProduct());\n    if (productOptional.isPresent()) {\n      return productOptional.get();\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseWorkflow.java",
    "content": "package io.temporal.samples.springboot.update;\n\nimport io.temporal.samples.springboot.update.model.Purchase;\nimport io.temporal.workflow.*;\n\n@WorkflowInterface\npublic interface PurchaseWorkflow {\n  @WorkflowMethod\n  void start();\n\n  @UpdateMethod\n  boolean makePurchase(Purchase purchase);\n\n  @UpdateValidatorMethod(updateName = \"makePurchase\")\n  void makePurchaseValidator(Purchase purchase);\n\n  @SignalMethod\n  void exit();\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/PurchaseWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.update;\n\nimport io.temporal.activity.LocalActivityOptions;\nimport io.temporal.failure.ApplicationFailure;\nimport io.temporal.samples.springboot.update.model.Purchase;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n@WorkflowImpl(taskQueues = \"UpdateSampleTaskQueue\")\npublic class PurchaseWorkflowImpl implements PurchaseWorkflow {\n\n  private boolean newPurchase = false;\n  private boolean exit = false;\n  private PurchaseActivities activities =\n      Workflow.newLocalActivityStub(\n          PurchaseActivities.class,\n          LocalActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public void start() {\n    // for sake of sample we only wait for a single purchase or exit signal\n    Workflow.await(() -> newPurchase || exit);\n  }\n\n  @Override\n  public boolean makePurchase(Purchase purchase) {\n\n    if (!activities.isProductInStockForPurchase(purchase)) {\n      throw ApplicationFailure.newFailure(\n          \"Product \"\n              + purchase.getProduct()\n              + \" is not in stock for amount \"\n              + purchase.getAmount(),\n          ProductNotAvailableForAmountException.class.getName(),\n          purchase);\n    }\n\n    return activities.makePurchase(purchase);\n  }\n\n  @Override\n  public void makePurchaseValidator(Purchase purchase) {\n    // Not allowed to change workflow state inside validator\n    // So invocations of (local) activities is prohibited\n    // We can validate the purchase with some business logic here\n\n    // Assume we have some max inventory amount for single item set to 100\n    if (purchase == null || (purchase.getAmount() < 0 || purchase.getAmount() > 100)) {\n      throw new IllegalArgumentException(\n          \"Invalid Product or amount (Product id:\"\n              + purchase.getProduct()\n              + \", amount\"\n              + purchase.getAmount()\n              + \")\");\n    }\n  }\n\n  @Override\n  public void exit() {\n    this.exit = true;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/README.md",
    "content": "# SpringBoot Synchronous Update Sample\n\n1. Start SpringBoot from main samples repo directory:\n\n       ./gradlew :springboot:bootRun\n\n2. In your browser navigate to:\n\n       http://localhost:3030/update\n\nPick one of the fishing items you want to purchase from the inventory drop down list.\nNext pick the amount of this item you want to purchase. \nThe inventory is presented in the table below the form.\nFor each item you can see the current availble stock count.\nTry first picking an item and then an amount that is less or equal to the items in \ninventory. You will see that the purchase goes through and the inventory table is updated\ndynamically.\n\nNow try to pick and item and amount that is greater than what's in our inventory.\nYou will see that the update fails and you see the \"Unable to perform purchase\" \nmessage that shows the underlying \"ProductNotAvailableForAmountException\" exception\nraised in the update handler. \n\nUpdating our inventory is done via local activities. The check if item and amount \nof the fishing item you want to purchase is in inventory is also done by local \nactivity.\n\n## Note\nMake sure that you enable the synchronous update feature on your Temporal cluster.\nThis can be done in dynamic config with\n\n        frontend.enableUpdateWorkflowExecution:\n           - value: true\n\nIf you don't have this enabled you will see error shown when you try to make any purchase."
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/ResourceNotFoundException.java",
    "content": "package io.temporal.samples.springboot.update;\n\nimport org.springframework.http.HttpStatus;\nimport org.springframework.web.bind.annotation.ResponseStatus;\n\n@ResponseStatus(value = HttpStatus.NOT_FOUND)\npublic class ResourceNotFoundException extends Exception {\n  public ResourceNotFoundException(String message) {\n    super(message);\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/model/Product.java",
    "content": "package io.temporal.samples.springboot.update.model;\n\nimport javax.persistence.*;\n\n@Entity\npublic class Product {\n  @Id\n  @GeneratedValue(strategy = GenerationType.IDENTITY)\n  private Integer id;\n\n  @Column(nullable = false)\n  public String name;\n\n  @Column(nullable = false)\n  public String code;\n\n  @Column(nullable = false)\n  public String description;\n\n  @Column(nullable = false)\n  public int price = 0;\n\n  @Column(nullable = false)\n  private int stock = 20;\n\n  public Product() {}\n\n  public Product(Integer id, String name, String code, String description, int price, int stock) {\n    this.id = id;\n    this.name = name;\n    this.code = code;\n    this.description = description;\n    this.price = price;\n    this.stock = stock;\n  }\n\n  public Integer getId() {\n    return id;\n  }\n\n  public void setId(Integer id) {\n    this.id = id;\n  }\n\n  public String getName() {\n    return name;\n  }\n\n  public void setName(String name) {\n    this.name = name;\n  }\n\n  public String getCode() {\n    return code;\n  }\n\n  public void setCode(String code) {\n    this.code = code;\n  }\n\n  public String getDescription() {\n    return description;\n  }\n\n  public void setDescription(String description) {\n    this.description = description;\n  }\n\n  public int getPrice() {\n    return price;\n  }\n\n  public void setPrice(int price) {\n    this.price = price;\n  }\n\n  public int getStock() {\n    return stock;\n  }\n\n  public void setStock(int stock) {\n    this.stock = stock;\n  }\n\n  public boolean removeStock() {\n    if (this.stock > 0) {\n      this.stock--;\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  public boolean removeStock(int value) {\n    if (this.stock > 0) {\n      this.stock -= value;\n      return true;\n    } else {\n      return false;\n    }\n  }\n\n  public void addStock() {\n    this.stock++;\n  }\n\n  public void addStock(int value) {\n    this.stock += value;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/model/ProductRepository.java",
    "content": "package io.temporal.samples.springboot.update.model;\n\nimport org.springframework.data.jpa.repository.JpaRepository;\nimport org.springframework.stereotype.Repository;\n\n@Repository\npublic interface ProductRepository extends JpaRepository<Product, Integer> {}\n"
  },
  {
    "path": "springboot/src/main/java/io/temporal/samples/springboot/update/model/Purchase.java",
    "content": "package io.temporal.samples.springboot.update.model;\n\npublic class Purchase {\n  int product;\n  int amount;\n\n  public Purchase() {}\n\n  public Purchase(int product, int amount) {\n    this.product = product;\n    this.amount = amount;\n  }\n\n  public int getProduct() {\n    return product;\n  }\n\n  public void setProduct(int product) {\n    this.product = product;\n  }\n\n  public int getAmount() {\n    return amount;\n  }\n\n  public void setAmount(int amount) {\n    this.amount = amount;\n  }\n}\n"
  },
  {
    "path": "springboot/src/main/resources/application-tc.yaml",
    "content": "spring.temporal:\n  namespace: <namespace_id> # https://docs.temporal.io/cloud/#temporal-cloud-namespace-id\n  connection:\n    target: <namespace_id>.tmprl.cloud:7233\n    mtls:\n      key-file: /path/to/key.key\n      cert-chain-file: /path/to/cert.pem\n\n# more configuration options https://github.com/temporalio/sdk-java/tree/master/temporal-spring-boot-autoconfigure#mtls"
  },
  {
    "path": "springboot/src/main/resources/application.yaml",
    "content": "server:\n  port: 3030\nspring:\n  main:\n    allow-bean-definition-overriding: true\n  application:\n    name: temporal-samples\n  # temporal specific configs\n  temporal:\n    namespace: default\n    connection:\n      target: 127.0.0.1:7233\n# (Note following configuration are not set by default but serve more as reference)\n#    workers:\n#      - task-queue: DemoTaskQueue\n#        capacity:\n#          max-concurrent-workflow-task-pollers: 6\n#          max-concurrent-activity-task-pollers: 6\n#        rate-limits:\n#          max-worker-activities-per-second: 3\n#          max-task-queue-activities-per-second: 3\n#    workflow-cache:\n#      max-instances: 10\n#      max-threads: 10\n    workersAutoDiscovery:\n      packages: io.temporal.samples.springboot\n  # data source config for some samples that need it\n  datasource:\n    url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;\n    username: sa\n    password: pass\n    driver-class-name: org.h2.Driver\n  jpa:\n    database-platform: org.hibernate.dialect.H2Dialect\n    hibernate:\n      ddl-auto: create-drop\n    defer-datasource-initialization: true\n  ## enable h2 console (h2-console endpoint) for debugging\n  h2:\n    console:\n      enabled: true\n  ## kafka setup for samples\n  kafka:\n    consumer:\n      auto-offset-reset: earliest\n    bootstrap-servers: localhost:9092\n# actuator (sdk metrics)\nmanagement:\n  endpoints:\n    web:\n      exposure:\n        include: prometheus,temporalworkerinfo\n# specific for samples\nsamples:\n  data:\n    language: english\n  message:\n    topic:\n      name: samples-topic\n    group:\n      name: samples-group\n"
  },
  {
    "path": "springboot/src/main/resources/data.sql",
    "content": "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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\n\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR1', '1/02/2004', 'Office Chair', 120);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR2', '1/05/2004', 'Office Desk', 200);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR3', '1/05/2004', 'Computer Mouse', 30);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR4', '1/08/2004', 'Mouse Pad', 19);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR5', '1/10/2004', 'Office Sofa', 170);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR6', '1/12/2004', 'MacBook Air', 980);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR7', '1/12/2004', 'Pens', 9);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR8', '1/12/2004', 'Printer Paper', 22);\nINSERT INTO officeorder(number, date, desc, price) VALUES ('OR9', '1/15/2004', 'Printer Cartridge', 110);"
  },
  {
    "path": "springboot/src/main/resources/static/js/jquery.sse.js",
    "content": "/*\n * jQuery Plugin for Server-Sent Events (SSE) EventSource Polyfill v0.1.3\n * https://github.com/byjg/jquery-sse\n *\n * This document is licensed as free software under the terms of the\n * MIT License: http://www.opensource.org/licenses/mit-license.php\n *\n * Copyright (c) 2015 by JG (João Gilberto Magalhães).\n */\n(function ($) {\n    $.extend({\n        SSE: function (url, customSettings) {\n            var sse = {instance: null, type: null};\n\n            var settings = {\n                onOpen: function (e) {\n                },\n                onEnd: function (e) {\n                },\n                onError: function (e) {\n                },\n                onMessage: function (e) {\n                },\n                options: {},\n                headers: {},\n                events: {}\n            };\n\n            $.extend(settings, customSettings);\n\n            sse._url = url;\n            sse._settings = settings;\n\n            // Start the proper EventSource object or Ajax fallback\n            sse._start = sse.start;\n            sse.start = function () {\n                if (this.instance) {\n                    return false;\n                }\n\n                if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) {\n                    createAjax(this);\n                } else {\n                    createEventSource(this);\n                }\n\n                return true;\n            };\n\n            // Stop the proper object\n            sse.stop = function () {\n                if (!this.instance) {\n                    return false;\n                }\n\n                if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) {\n                    // Nothing to do;\n                } else {\n                    this.instance.close();\n                }\n                this._settings.onEnd();\n\n                this.instance = null;\n                this.type = null;\n\n                return true;\n\n            };\n\n            return sse;\n\n        }\n    });\n\n\n    // Private Method for Handle EventSource object\n    function createEventSource(me) {\n        me.type = 'event';\n        me.instance = new EventSource(me._url);\n        me.instance.successCount = 0;\n\n        me.instance.onmessage = me._settings.onMessage;\n        me.instance.onopen = function (e) {\n            if (me.instance.successCount++ === 0) {\n                me._settings.onOpen(e);\n            }\n        };\n        me.instance.onerror = function (e) {\n            if (e.target.readyState === EventSource.CLOSED) {\n                me._settings.onError(e);\n            }\n        };\n\n        for (var key in me._settings.events) {\n            me.instance.addEventListener(key, me._settings.events[key], false);\n        }\n    }\n\n    // Handle the Ajax instance (fallback)\n    function createAjax(me) {\n        me.type = 'ajax';\n        me.instance = {successCount: 0, id: null, retry: 3000, data: \"\", event: \"\"};\n        runAjax(me);\n    }\n\n    // Handle the continous Ajax request (fallback)\n    function runAjax(me) {\n        if (!me.instance) {\n            return;\n        }\n\n        var headers = {'Last-Event-ID': me.instance.id};\n\n        $.extend(headers, me._settings.headers);\n\n        $.ajax({\n            url: me._url,\n            method: 'GET',\n            headers: headers,\n            success: function (receivedData, status, info) {\n                if (!me.instance) {\n                    return;\n                }\n\n                if (me.instance.successCount++ === 0) {\n                    me._settings.onOpen();\n                }\n\n                var lines = receivedData.split(\"\\n\");\n\n                // Process the return to generate a compatible SSE response\n                me.instance.data = \"\";\n                var countBreakLine = 0;\n                for (var key in lines) {\n                    var separatorPos = lines[key].indexOf(\":\");\n                    var item = [\n                        lines[key].substr(0, separatorPos),\n                        lines[key].substr(separatorPos + 1)\n                    ];\n                    switch (item[0]) {\n                        // If the first part is empty, needed to check another sequence\n                        case \"\":\n                            if (!item[1] && countBreakLine++ === 1) {  // Avoid comments!\n                                eventMessage = {\n                                    data: me.instance.data,\n                                    lastEventId: me.instance.id,\n                                    origin: 'http://' + info.getResponseHeader('Host'),\n                                    returnValue: true\n                                };\n\n                                // If there are a custom event then call it\n                                if (me.instance.event && me._settings.events[me.instance.event]) {\n                                    me._settings.events[me.instance.event](eventMessage);\n                                } else {\n                                    me._settings.onMessage(eventMessage);\n                                }\n                                me.instance.data = \"\";\n                                me.instance.event = \"\";\n                                countBreakLine = 0;\n                            }\n                            break;\n\n                        // Define the new retry object;\n                        case \"retry\":\n                            countBreakLine = 0;\n                            me.instance.retry = parseInt(item[1].trim());\n                            break;\n\n                        // Define the new ID\n                        case \"id\":\n                            countBreakLine = 0;\n                            me.instance.id = item[1].trim();\n                            break;\n\n                        // Define a custom event\n                        case \"event\":\n                            countBreakLine = 0;\n                            me.instance.event = item[1].trim();\n                            break;\n\n                        // Define the data to be processed.\n                        case \"data\":\n                            countBreakLine = 0;\n                            me.instance.data += (me.instance.data !== \"\" ? \"\\n\" : \"\") + item[1].trim();\n                            break;\n\n                        default:\n                            countBreakLine = 0;\n                    }\n                }\n                setTimeout(function () {\n                    runAjax(me);\n                }, me.instance.retry);\n            },\n            error: me._settings.onError\n        });\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "springboot/src/main/resources/static/js/samplessse.js",
    "content": "$( document ).ready(function() {\n\n    var sse = $.SSE('/kafka-messages', {\n        onMessage: function(e){\n            console.log(e);\n            $('#kafka-messages tr:last').after('<tr><td>'+e.data+'</td></tr>');\n        },\n        onError: function(e){\n            sse.stop();\n            console.log(\"Could not connect..Stopping SSE\");\n        },\n        onEnd: function(e){\n            console.log(\"End\");\n        }\n    });\n    sse.start();\n\n});"
  },
  {
    "path": "springboot/src/main/resources/templates/actuator.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\" xmlns=\"http://www.w3.org/1999/html\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>In this sample we show how to create a custom Actuator Endpoint showing Worker Info.</h6>\n            <br/><br/>\n            <div>\n                <h6><a href=\"https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html\" target=\"_blank\">Spring Actuator</a> allows\n                    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.</h6><br/>\n                View the custom endpoint at <a href=\"http://localhost:3030/actuator/temporalworkerinfo\" target=\"_blank\">localhost:3030/actuator/temporalworkerinfo</a>\n            </div>\n        </div>\n    </div>\n    <footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/camel.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\" xmlns=\"http://www.w3.org/1999/html\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This sample shows how to start Workflow execution from an Apache Camel Route</h6>\n            <br/><br/><br/>\n            <div>\n                <p><a href=\"https://camel.apache.org/\" target=\"_blank\">Apache Camel</a> is an integration framework\n                    with a large number of out-of box integrations with many different apis.\n                    In this sample we show how to integrate Temporal as part of a Camel Route which define steps\n                    of messages from its source to destination. It allows you to bring numerous\n                    advantages of Temporal such as high reliability and fault tolerance into your existing Camel applications.\n                </p>\n                <p>To run the sample navigate to <a href=\"http://localhost:3030/orders\" target=\"_blank\">localhost:3030/orders</a>\n                    <br/>This endpoint starts a Camel Route which in turn starts our Workflow executipn and delivers results.\n                </p>\n            </div>\n        </div>\n    </div>\n</div>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/customize.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This sample shows how to optimize default options such as\n                <ul>\n                    <li>WorkflowServiceStubsOptions</li>\n                    <li>WorkflowClientOption</li>\n                    <li>WorkerFactoryOptions</li>\n                    <li>WorkerOptions</li>\n                </ul>\n                <br/>WorkerOptions can be optimized per worker/task queue.\n                For this sample we set our specific worker to be \"local activity worker\" via custom options meaning\n                it would not poll for activity tasks. Click on \"Run Workflow\" button to start instance of\n                our sample workflow. This workflow will try to invoke activity as \"normal\" activity which should\n                timeout on the set ScheduleToClose timeout, we handle this activity failure\n                and then invoke this activity as local activity which should succeed and update the\n                workflow execution result on the page.\n            </h6>\n            <div class=\"form-group\">\n                <br/><br/><br/>\n                <form action=\"/customize\", id=\"sampleform\">\n                    <p><input type=\"submit\" value=\"Run Workflow\" class=\"btn btn-primary\" />\n                </form>\n            </div>\n        </div>\n        <div style=\"width: 18rem;\">\n            <div>\n                <h5 class=\"card-title\">Workflow result:</h5>\n                <div id=\"result\"></div>\n            </div>\n        </div>\n    </div>\n</div>\n<script>\n    $(\"#sampleform\").submit(function( event ) {\n        event.preventDefault();\n        $( \"#result\" ).empty().append( \"\" );\n\n        var $form = $( this ),\n            url = $form.attr( \"action\" );\n\n        $.ajax({\n            'url': url,\n            'method':'POST',\n            'contentType': 'application/json',\n            success: function(response) {\n                $( \"#result\" ).empty().append( response );\n            }\n        });\n    });\n</script>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/fragments.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head th:fragment=\"samples-header\">\n    <link rel=\"stylesheet\"\n          href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css\"/>\n    <link rel=\"stylesheet\"\n          href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.min.css\"/>\n    <link href=\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@1,400;1,700&display=swap\" rel=\"stylesheet\">\n    <script src=\"https://code.jquery.com/jquery-3.3.1.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js\"></script>\n    <script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/prism.min.js\"></script>\n</head>\n<body>\n<footer class=\"text-center text-lg-start bg-light text-muted\" th:fragment=\"samples-footer\">\n    <div class=\"me-5 d-none d-lg-block\">\n        <a href=\"/\"> Back to Samples </a>\n    </div>\n</footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/hello.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This is a simple greeting example. Enter persons first and last name then submit form to start\n            workflow execution. Results will be updated on the page.</h6>\n            <div class=\"form-group\">\n                <br/><br/><br/>\n                <h5>Say hello to:</h5>\n                <form action=\"/hello\", id=\"sampleform\">\n                    <p>First Name: <input type=\"text\" name=\"firstName\"/></p>\n                    <p>Last Name: <input type=\"text\" name=\"lastName\"/></p>\n                    <p><input type=\"submit\" value=\"Run Workflow\" class=\"btn btn-primary\" />\n                        <input type=\"reset\" value=\"Reset Form\" class=\"btn btn-secondary\" />\n                </form>\n            </div>\n        </div>\n        <div style=\"width: 18rem;\">\n            <div>\n                <h5 class=\"card-title\">Workflow result:</h5>\n                <div id=\"result\"></div>\n            </div>\n        </div>\n    </div>\n</div>\n<script>\n$(\"#sampleform\").submit(function( event ) {\n    event.preventDefault();\n\n    var $form = $( this ),\n        firstName = $form.find( \"input[name='firstName']\" ).val(),\n        lastName = $form.find( \"input[name='lastName']\" ).val(),\n        url = $form.attr( \"action\" );\n\n    $.ajax({\n        'url': url,\n        'method':'POST',\n        'dataType': 'json',\n        'contentType': 'application/json',\n        'data':JSON.stringify({\n            \"firstName\": firstName,\n            \"lastName\": lastName\n        }),\n        success: function(response) {\n            $( \"#result\" ).empty().append( response );\n        }\n    });\n});\n</script>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h5 class=\"card-title\">Temporal Java SDK Samples</h5>\n            <p class=\"card-text\">Click on sample link to run it.</p>\n            <div class=\"list-group\">\n                <a href=\"/hello\" class=\"list-group-item list-group-item-action\">Say Hello</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/metrics\" class=\"list-group-item list-group-item-action\">SDK Metrics</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/update\" class=\"list-group-item list-group-item-action\">Synchronous Update</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/kafka\" class=\"list-group-item list-group-item-action\">Kafka Request/Reply</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/customize\" class=\"list-group-item list-group-item-action\">Customize Options</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/camel\" class=\"list-group-item list-group-item-action\">Apache Camel Route</a>\n            </div>\n            <div class=\"list-group\">\n                <a href=\"/customendpoint\" class=\"list-group-item list-group-item-action\">Custom Actuator Endpoint - Worker Info</a>\n            </div>\n        </div>\n    </div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/kafka.html",
    "content": "<!DOCTYPE HTML>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This sample shows how we can trigger and signal workflow executions by sending messages\n            to Kafka topic. It also shows how workflows can send updates and result to Kafka for other\n            services to consume. <br/>Enter a message in the form and submit. This will trigger a workflow\n            and the messages it sends to Kafka are shown on the page dynamically.</h6>\n            <br/><br/><br/>\n            <div class=\"form-group\">\n                <form action=\"/kafka\", id=\"sampleform\">\n                    <p>Message: <input type=\"text\" name=\"message\"/></p>\n                    <p><input type=\"submit\" value=\"Send Message\" class=\"btn btn-primary\" />\n                </form>\n            </div>\n        </div>\n    </div>\n    <div class=\"row\" >\n        <div class=\"col-md-6\">\n            <div class=\"panel panel-default\" th:fragment=\"MessageList\" id=\"MessageData\">\n                <div class=\"panel-body\">\n                    <div class=\"table-responsive\">\n                        <table class=\"table table-striped\" id=\"kafka-messages\">\n                            <thead>\n                            <tr>\n                                <th>Kafka Messages:</th>\n                            </tr>\n                            </thead>\n                        </table>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n<script type=\"text/javascript\" src=\"/js/jquery.sse.js\"></script>\n<script type=\"text/javascript\" src=\"/js/samplessse.js\"></script>\n<script>\n    $(\"#sampleform\").submit(function( event ) {\n        event.preventDefault();\n\n        var $form = $( this ),\n            message = $form.find( \"input[name='message']\" ).val(),\n            url = $form.attr( \"action\" );\n\n        $.ajax({\n            'url': url,\n            'method':'POST',\n            'dataType': 'text',\n            'contentType': 'text/plain',\n            'data': message,\n            success: function(response) {\n                // omit, message sent from activities to kafka topic will update\n            }\n        });\n    });\n</script>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/metrics.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\" xmlns=\"http://www.w3.org/1999/html\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>In this sample we show how to set up and consume SDK metrics.</h6>\n            <br/><br/><br/>\n            <div>\n                <h6>Configuring SDK metrics is super easy using <a href=\"https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html\" target=\"_blank\">Spring Actuator</a></h6><br/>\n                <div class=\"list-group\">\n                    <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start\">\n                        <div class=\"d-flex w-100 justify-content-between\">\n                            <h5 class=\"mb-1\">1. Add spring-boot-starter-actuator to project dependencies</h5>\n                        </div>\n                        <p class=\"mb-1\">\n                        <pre><code>Gradle:\n                        <br/> implementation \"org.springframework.boot:spring-boot-starter-actuator\"\n                         </code></pre>\n                        <pre><code>Maven:\n                        <br/> &lt;dependency&gt;\n   &lt;groupId&gt;org.springframework.boot&lt;/groupId&gt;\n   &lt;artifactId&gt;spring-boot-starter-actuator&lt;/artifactId&gt;\n &lt;/dependency&gt;\n                         </code></pre>\n                        </p>\n                    </a>\n                    <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start\">\n                        <div class=\"d-flex w-100 justify-content-between\">\n                            <h5 class=\"mb-1\">2. Add micrometer-registry-prometheus as runtime dependency</h5>\n                        </div>\n                        <p class=\"mb-1\">\n                        <pre><code>Gradle:\n                        <br/> runtimeOnly \"io.micrometer:micrometer-registry-prometheus\"\n                         </code></pre>\n                        <pre><code>Maven:\n                        <br/> &lt;dependency&gt;\n   &lt;groupId>io.micrometer&lt;/groupId&gt;\n   &lt;artifactId>micrometer-registry-prometheus&lt;/artifactId&gt;\n   &lt;scope>runtime&lt;/scope&gt;\n &lt;/dependency>\n                         </code></pre>\n                        </p>\n                    </a>\n                    <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start\">\n                        <div class=\"d-flex w-100 justify-content-between\">\n                            <h5 class=\"mb-1\">3. Configure Actuator in application properties/yaml</h5>\n                        </div>\n                        <p class=\"mb-1\">\n                        <pre><code>management:\n  endpoints:\n    web:\n      exposure:\n        include: prometheus\n                         </code></pre>\n                         <small class=\"text-muted\">Alternatively, you can define a custom io.micrometer.core.instrument.MeterRegistry bean in the application context.</small>\n                    </a>\n                    <a href=\"#\" class=\"list-group-item list-group-item-action flex-column align-items-start\">\n                        <div class=\"d-flex w-100 justify-content-between\">\n                            <h5 class=\"mb-1\">4. View metrics (Prometheus format)</h5>\n                        </div>\n                        <p>Actuator will expose SDK metrics automatically.<br/>\n                            You can access raw metrics at \"localhost:3030/actuator/prometheus\".<br/>\n                            This is already set up for the samples project.\n                        </p>\n                        <small class=\"text-muted\">You can set up Prometheus config to scrape SDK metrics from this endpoint.</small>\n                    </a>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/main/resources/templates/update.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This sample shows use of Synchronous Update feature. The page shows a form used to make a purchase.\n            <br/>Under the form you can see the total inventory of our fishing items.\n            Inventory has a pre-set stock (number of each item available). Once you make a purchase our workflow\n            is sent an update, meaning we want to make the purchase and respond back when its done.\n            <br/>The workflow update validator checks if the given amount of an item is still available in the inventory.\n            <br/>If it is then the update goes through and the inventory table is dynamically updated.\n            If not then the update is rejected and shown on the screen.</h6>\n            <br/>\n            <div class=\"container\">\n                <div class=\"row\">\n                    <div class=\"col-xs-6\">\n                        <h4>Purchase Form</h4>\n                        <form action=\"/update/purchase\", id=\"inventoryform\">\n                            <div class=\"form-group\">\n                                <p>Select product:\n                                    <select class=\"form-control\" id=\"productlist\">\n                                    <option th:each=\"product : ${products}\" th:value=\"${product.id}\" th:text=\"${product.name}\"></option>\n                                </select>\n                                </p>\n                                <label class=\"form-label\" for=\"purchaseNumber\">Enter amount:</label>\n                                <input type=\"number\" id=\"purchaseNumber\" class=\"form-control\" />\n                                <br/>\n                                <p><input type=\"submit\" value=\"Make purchase\" class=\"btn btn-primary\" />\n                            </div>\n                        </form>\n                        <br/>\n                        <div class=\"alert alert-dismissible\" role=\"alert\" style=\"display:none;\">\n                            <strong>Some default title!</strong>\n                            <p>Some Default message</p>\n                        </div>\n                    </div>\n                    <div class=\"col-xs-6\">\n                        <br/><br/>\n                        <h4>Inventory</h4>\n                        <div class=\"inventory_data\">\n                        <table class=\"table table-striped\" th:fragment=\"inventory\">\n                            <thead class=\"thead-dark\">\n                            <tr>\n                                <th>Id</th>\n                                <th>Name</th>\n                                <th>Description</th>\n                                <th>Price ($)</th>\n                                <th>Stock (Available)</th>\n                            </tr>\n                            </thead>\n                            <tbody>\n                            <tr th:each=\"product : ${products}\">\n                                <td th:text=\"${product.id}\">Id</td>\n                                <td th:text=\"${product.name}\">Name</td>\n                                <td th:text=\"${product.description}\">Description</td>\n                                <td th:text=\"${product.price}\">Price</td>\n                                <td th:text=\"${product.stock}\">Stock</td>\n                            </tr>\n                            </tbody>\n                        </table>\n                        </div>\n                    </div>\n                </div>\n            </div>\n        </div>\n    </div>\n</div>\n<script>\n    $(\"#inventoryform\").submit(function( event ) {\n        event.preventDefault();\n\n        var $form = $( this ),\n            product = $form.find( \"select\" ).val(),\n            amount = $form.find( \"input[id='purchaseNumber']\" ).val(),\n            url = $form.attr( \"action\" );\n\n        var alertMsg = $('div[role=\"alert\"]');\n        alertMsg.hide();\n\n        $.ajax({\n            'url': url,\n            'method':'POST',\n            'dataType': 'json',\n            'contentType': 'application/json',\n            'data':JSON.stringify({\n                \"product\": product,\n                \"amount\": amount\n            }),\n            success: function(response) {\n                $.ajax({\n                    'url': '/update/inventory',\n                    'method':'GET',\n                    success: function(response) {\n                        $('.inventory_data').html(response);\n                        showPurchaseResponse(\"Purchases successful\",\n                            \"Your purchase was validated and performed.\", \"success\")\n                    },\n                    error: function (err) {\n                        showPurchaseResponse(\"Unable to perform purchase\",\n                            err.responseText, \"danger\")\n                    }\n                });\n            },\n            error: function (err) {\n                showPurchaseResponse(\"Unable to perform purchase\",\n                    err.responseText, \"danger\")\n            }\n        });\n    });\n\n    function showPurchaseResponse(msg_title, msg_body, msg_type) {\n        var alertMsg = $('div[role=\"alert\"]');\n        $(alertMsg).find('strong').html(msg_title);\n        $(alertMsg).find('p').html(msg_body);\n        $(alertMsg).removeAttr('class');\n        $(alertMsg).addClass('alert alert-' + msg_type);\n        $(alertMsg).show();\n    }\n</script>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/CamelSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.camel.OfficeOrder;\nimport io.temporal.samples.springboot.camel.OrderWorkflow;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport java.util.List;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = HelloSampleTest.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n// set this to omit setting up embedded kafka\n@EnableAutoConfiguration(exclude = {KafkaAutoConfiguration.class})\npublic class CamelSampleTest {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testOrdersWorkflow() {\n    OrderWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            OrderWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"CamelSampleTaskQueue\")\n                .setWorkflowId(\"CamelSampleWorkflow\")\n                .build());\n    List<OfficeOrder> result = workflow.start();\n    Assert.notNull(result, \"Result should not be null\");\n    Assert.isTrue(result.size() == 9, \"Invalid result\");\n  }\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/CustomizeSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.customize.CustomizeWorkflow;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = CustomizeSampleTest.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n// set this to omit setting up embedded kafka\n@EnableAutoConfiguration(exclude = {KafkaAutoConfiguration.class})\n@DirtiesContext\npublic class CustomizeSampleTest {\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testHello() {\n    CustomizeWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            CustomizeWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"CustomizeTaskQueue\")\n                .setWorkflowId(\"CustomizeSampleTest\")\n                .build());\n    String result = workflow.execute();\n    Assert.notNull(result, \"Result should not be null\");\n    Assert.isTrue(result.equals(\"Completed as Local activity!\"), \"Invalid result\");\n  }\n\n  @ComponentScan\n  public static class Configuration {}\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/HelloSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.hello.HelloWorkflow;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = HelloSampleTest.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n// set this to omit setting up embedded kafka\n@EnableAutoConfiguration(exclude = {KafkaAutoConfiguration.class})\n@DirtiesContext\npublic class HelloSampleTest {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testHello() {\n    HelloWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            HelloWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"HelloSampleTaskQueue\")\n                .setWorkflowId(\"HelloSampleTest\")\n                .build());\n    String result = workflow.sayHello(new Person(\"Temporal\", \"User\"));\n    Assert.notNull(result, \"Greeting should not be null\");\n    Assert.isTrue(result.equals(\"Hello Temporal User!\"), \"Invalid result\");\n  }\n\n  @ComponentScan\n  public static class Configuration {}\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/HelloSampleTestMockedActivity.java",
    "content": "package io.temporal.samples.springboot;\n\nimport static org.mockito.ArgumentMatchers.any;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.hello.HelloActivity;\nimport io.temporal.samples.springboot.hello.HelloActivityImpl;\nimport io.temporal.samples.springboot.hello.HelloWorkflow;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Captor;\nimport org.mockito.Mockito;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.test.mock.mockito.MockBean;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.context.annotation.Primary;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = HelloSampleTestMockedActivity.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n// set this to omit setting up embedded kafka\n@EnableAutoConfiguration(exclude = {KafkaAutoConfiguration.class})\n@DirtiesContext\npublic class HelloSampleTestMockedActivity {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @Captor ArgumentCaptor<Person> personArgumentCaptor;\n\n  @Autowired HelloActivity activity;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testHello() {\n    HelloWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            HelloWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"HelloSampleTaskQueue\")\n                .setWorkflowId(\"HelloSampleTest\")\n                .build());\n    String result = workflow.sayHello(new Person(\"Temporal\", \"User\"));\n    Assert.notNull(result, \"Greeting should not be null\");\n    Assert.isTrue(result.equals(\"Hello from mocked activity\"), \"Invalid result\");\n\n    Mockito.verify(activity, Mockito.times(1)).hello(personArgumentCaptor.capture());\n    Assert.notNull(personArgumentCaptor.getValue(), \"Invalid input\");\n    Assert.isTrue(\n        personArgumentCaptor.getValue().getFirstName().equals(\"Temporal\"),\n        \"Invalid person first name\");\n    Assert.isTrue(\n        personArgumentCaptor.getValue().getLastName().equals(\"User\"), \"invalid person last name\");\n  }\n\n  @ComponentScan\n  public static class Configuration {\n    @MockBean private HelloActivityImpl helloActivityMock;\n\n    @Bean\n    @Primary\n    public HelloActivity getTestActivityImpl() {\n      Mockito.when(helloActivityMock.hello(any())).thenReturn(\"Hello from mocked activity\");\n      return helloActivityMock;\n    }\n  }\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/KafkaConsumerTestHelper.java",
    "content": "package io.temporal.samples.springboot;\n\nimport java.util.concurrent.CountDownLatch;\nimport org.apache.kafka.clients.consumer.ConsumerRecord;\nimport org.springframework.kafka.annotation.KafkaListener;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class KafkaConsumerTestHelper {\n  private CountDownLatch latch = new CountDownLatch(5);\n  private String payload = null;\n\n  @KafkaListener(id = \"samples-test-id\", topics = \"${samples.message.topic.name}\")\n  public void receive(ConsumerRecord<?, ?> consumerRecord) {\n\n    setPayload(consumerRecord.toString());\n    latch.countDown();\n  }\n\n  public CountDownLatch getLatch() {\n    return latch;\n  }\n\n  public String getPayload() {\n    return payload;\n  }\n\n  private void setPayload(String payload) {\n    this.payload = payload;\n  }\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/KafkaSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.client.WorkflowStub;\nimport io.temporal.samples.springboot.kafka.MessageWorkflow;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.kafka.test.context.EmbeddedKafka;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = KafkaSampleTest.Configuration.class)\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n@EmbeddedKafka(\n    partitions = 1,\n    bootstrapServersProperty = \"spring.kafka.bootstrap-servers\",\n    controlledShutdown = true)\n@DirtiesContext\npublic class KafkaSampleTest {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @Autowired KafkaConsumerTestHelper consumer;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testKafkaWorflow() throws Exception {\n    MessageWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            MessageWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"KafkaSampleTaskQueue\")\n                .setWorkflowId(\"NewMessageWorkflow\")\n                .build());\n\n    WorkflowClient.start(workflow::start);\n    workflow.update(\"This is a test message\");\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n    consumer.getLatch().await();\n\n    Assert.isTrue(consumer.getLatch().getCount() == 0L, \"Invalid latch count\");\n    Assert.isTrue(\n        consumer.getPayload().contains(\"Completing execution: NewMessageWorkflow\"),\n        \"Invalid last event payload\");\n  }\n\n  @ComponentScan\n  public static class Configuration {}\n}\n"
  },
  {
    "path": "springboot/src/test/java/io/temporal/samples/springboot/UpdateSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport static org.junit.jupiter.api.Assertions.assertThrows;\n\nimport io.temporal.client.*;\nimport io.temporal.samples.springboot.update.PurchaseWorkflow;\nimport io.temporal.samples.springboot.update.model.Purchase;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ActiveProfiles;\n\n@SpringBootTest(classes = HelloSampleTest.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n// set this to omit setting up embedded kafka\n@EnableAutoConfiguration(exclude = {KafkaAutoConfiguration.class})\n@DirtiesContext\npublic class UpdateSampleTest {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testUpdate() {\n    PurchaseWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            PurchaseWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"UpdateSampleTaskQueue\")\n                .setWorkflowId(\"NewPurchase\")\n                .build());\n    Purchase purchase = new Purchase(1, 3);\n    WorkflowClient.start(workflow::start);\n    // send update\n    workflow.makePurchase(purchase);\n    workflow.exit();\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n  }\n\n  @Test()\n  public void testUpdateRejected() {\n    PurchaseWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            PurchaseWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"UpdateSampleTaskQueue\")\n                .setWorkflowId(\"NewPurchase\")\n                .build());\n    Purchase purchase = new Purchase(1, 40);\n    WorkflowClient.start(workflow::start);\n    // send update\n    assertThrows(\n        WorkflowUpdateException.class,\n        () -> {\n          workflow.makePurchase(purchase);\n        });\n    workflow.exit();\n    WorkflowStub.fromTyped(workflow).getResult(Void.class);\n  }\n\n  @ComponentScan\n  public static class Configuration {}\n}\n"
  },
  {
    "path": "springboot/src/test/resources/application.yaml",
    "content": "server:\n  port: 3030\nspring:\n  main:\n    allow-bean-definition-overriding: true\n  application:\n    name: temporal-samples\n  # temporal specific configs\n  temporal:\n    connection:\n      target: 127.0.0.1:7233\n      target.namespace: default\n    workersAutoDiscovery:\n      packages: io.temporal.samples.springboot\n    # enable test server for testing\n    test-server:\n      enabled: true\n    ignore-duplicate-definitions: true\n  # data source config for tests that need it\n  datasource:\n    url: jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE;\n    username: sa\n    password: pass\n    driver-class-name: org.h2.Driver\n  jpa:\n    database-platform: org.hibernate.dialect.H2Dialect\n    hibernate:\n      ddl-auto: create-drop\n    defer-datasource-initialization: true\n  ## kafka setup for samples\n  kafka:\n    consumer:\n      auto-offset-reset: earliest\n    bootstrap-servers: ${spring.embedded.kafka.brokers}\n# specific for samples\nsamples:\n  data:\n    language: english\n  message:\n    topic:\n      name: samples-test-topic\n    group:\n      name: samples-group\n"
  },
  {
    "path": "springboot/src/test/resources/data.sql",
    "content": "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);\nINSERT 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);\nINSERT 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);\nINSERT 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);\nINSERT 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);"
  },
  {
    "path": "springboot-basic/build.gradle",
    "content": "apply plugin: 'org.springframework.boot'\n\ndependencies {\n    implementation \"org.springframework.boot:spring-boot-starter-web\"\n    implementation \"org.springframework.boot:spring-boot-starter-thymeleaf\"\n    implementation \"org.springframework.boot:spring-boot-starter-actuator\"\n    implementation \"io.temporal:temporal-spring-boot-starter:$javaSDKVersion\"\n    testImplementation \"org.springframework.boot:spring-boot-starter-test\"\n    runtimeOnly \"io.micrometer:micrometer-registry-prometheus\"\n    dependencies {\n        errorproneJavac('com.google.errorprone:javac:9+181-r4173-1')\n        errorprone('com.google.errorprone:error_prone_core:2.28.0')\n    }\n}\n\nbootJar {\n    enabled = false\n}\n\njar {\n    enabled = true\n}"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/SamplesController.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.hello.HelloWorkflow;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.http.HttpStatus;\nimport org.springframework.http.MediaType;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.stereotype.Controller;\nimport org.springframework.ui.Model;\nimport org.springframework.web.bind.annotation.*;\n\n@Controller\npublic class SamplesController {\n\n  @Autowired WorkflowClient client;\n\n  @GetMapping(\"/hello\")\n  public String hello(Model model) {\n    model.addAttribute(\"sample\", \"Say Hello\");\n    return \"hello\";\n  }\n\n  @PostMapping(\n      value = \"/hello\",\n      consumes = {MediaType.APPLICATION_JSON_VALUE},\n      produces = {MediaType.TEXT_HTML_VALUE})\n  ResponseEntity<String> helloSample(@RequestBody Person person) {\n    HelloWorkflow workflow =\n        client.newWorkflowStub(\n            HelloWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"HelloSampleTaskQueue\")\n                .setWorkflowId(\"HelloSample\")\n                .build());\n\n    // bypass thymeleaf, don't return template name just result\n    return new ResponseEntity<>(\"\\\"\" + workflow.sayHello(person) + \"\\\"\", HttpStatus.OK);\n  }\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/TemporalSpringbootSamplesApplication.java",
    "content": "package io.temporal.samples.springboot;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconfigure.SpringBootApplication;\n\n@SpringBootApplication\npublic class TemporalSpringbootSamplesApplication {\n  public static void main(String[] args) {\n    SpringApplication.run(TemporalSpringbootSamplesApplication.class, args).start();\n  }\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloActivity.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.activity.ActivityInterface;\nimport io.temporal.samples.springboot.hello.model.Person;\n\n@ActivityInterface\npublic interface HelloActivity {\n  String hello(Person person);\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloActivityImpl.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.spring.boot.ActivityImpl;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Component;\n\n@Component\n@ActivityImpl(taskQueues = \"HelloSampleTaskQueue\")\npublic class HelloActivityImpl implements HelloActivity {\n  @Value(\"${samples.data.language}\")\n  private String language;\n\n  @Override\n  public String hello(Person person) {\n    String greeting = language.equals(\"spanish\") ? \"Hola \" : \"Hello \";\n    return greeting + person.getFirstName() + \" \" + person.getLastName() + \"!\";\n  }\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflow.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.workflow.WorkflowInterface;\nimport io.temporal.workflow.WorkflowMethod;\n\n@WorkflowInterface\npublic interface HelloWorkflow {\n  @WorkflowMethod\n  String sayHello(Person person);\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/HelloWorkflowImpl.java",
    "content": "package io.temporal.samples.springboot.hello;\n\nimport io.temporal.activity.ActivityOptions;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.spring.boot.WorkflowImpl;\nimport io.temporal.workflow.Workflow;\nimport java.time.Duration;\n\n@WorkflowImpl(taskQueues = \"HelloSampleTaskQueue\")\npublic class HelloWorkflowImpl implements HelloWorkflow {\n\n  private HelloActivity activity =\n      Workflow.newActivityStub(\n          HelloActivity.class,\n          ActivityOptions.newBuilder().setStartToCloseTimeout(Duration.ofSeconds(2)).build());\n\n  @Override\n  public String sayHello(Person person) {\n    return activity.hello(person);\n  }\n}\n"
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/README.md",
    "content": "# SpringBoot Hello Sample\n\n1. Start SpringBoot from main samples repo directory:\n   \n       ./gradlew :springboot-basic:bootRun\n\n2. In your browser navigate to:\n \n       http://localhost:3030/hello\n\nEnter in first and last name in the form then click on Run Workflow\nto start workflow execution. Page will be updated to show the workflow\nexecution results (the greeting).\n\nYou can try changing the language setting in [application.yaml](../../../../../../resources/application.yaml) file\nfrom \"english\" to \"spanish\" to get the greeting result in Spanish."
  },
  {
    "path": "springboot-basic/src/main/java/io/temporal/samples/springboot/hello/model/Person.java",
    "content": "package io.temporal.samples.springboot.hello.model;\n\npublic class Person {\n  private String firstName;\n  private String lastName;\n\n  public Person() {}\n\n  public Person(String firstName, String lastName) {\n    this.firstName = firstName;\n    this.lastName = lastName;\n  }\n\n  public String getFirstName() {\n    return firstName;\n  }\n\n  public void setFirstName(String firstName) {\n    this.firstName = firstName;\n  }\n\n  public String getLastName() {\n    return lastName;\n  }\n\n  public void setLastName(String lastName) {\n    this.lastName = lastName;\n  }\n}\n"
  },
  {
    "path": "springboot-basic/src/main/resources/application-tc.yaml",
    "content": "spring.temporal:\n  namespace: <namespace_id> # https://docs.temporal.io/cloud/#temporal-cloud-namespace-id\n  connection:\n    target: <namespace_id>.tmprl.cloud:7233\n    mtls:\n      key-file: /path/to/key.key\n      cert-chain-file: /path/to/cert.pem\n\n# more configuration options https://github.com/temporalio/sdk-java/tree/master/temporal-spring-boot-autoconfigure#mtls"
  },
  {
    "path": "springboot-basic/src/main/resources/application.yaml",
    "content": "server:\n  port: 3030\nspring:\n  main:\n    allow-bean-definition-overriding: true\n  application:\n    name: temporal-samples\n  # temporal specific configs\n  temporal:\n    namespace: default\n    connection:\n      target: 127.0.0.1:7233\n    workersAutoDiscovery:\n      packages: io.temporal.samples.springboot\n# specific for samples\nsamples:\n  data:\n    language: english\n"
  },
  {
    "path": "springboot-basic/src/main/resources/static/js/jquery.sse.js",
    "content": "/*\n * jQuery Plugin for Server-Sent Events (SSE) EventSource Polyfill v0.1.3\n * https://github.com/byjg/jquery-sse\n *\n * This document is licensed as free software under the terms of the\n * MIT License: http://www.opensource.org/licenses/mit-license.php\n *\n * Copyright (c) 2015 by JG (João Gilberto Magalhães).\n */\n(function ($) {\n    $.extend({\n        SSE: function (url, customSettings) {\n            var sse = {instance: null, type: null};\n\n            var settings = {\n                onOpen: function (e) {\n                },\n                onEnd: function (e) {\n                },\n                onError: function (e) {\n                },\n                onMessage: function (e) {\n                },\n                options: {},\n                headers: {},\n                events: {}\n            };\n\n            $.extend(settings, customSettings);\n\n            sse._url = url;\n            sse._settings = settings;\n\n            // Start the proper EventSource object or Ajax fallback\n            sse._start = sse.start;\n            sse.start = function () {\n                if (this.instance) {\n                    return false;\n                }\n\n                if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) {\n                    createAjax(this);\n                } else {\n                    createEventSource(this);\n                }\n\n                return true;\n            };\n\n            // Stop the proper object\n            sse.stop = function () {\n                if (!this.instance) {\n                    return false;\n                }\n\n                if (!window.EventSource || this._settings.options.forceAjax || (Object.keys(this._settings.headers).length > 0)) {\n                    // Nothing to do;\n                } else {\n                    this.instance.close();\n                }\n                this._settings.onEnd();\n\n                this.instance = null;\n                this.type = null;\n\n                return true;\n\n            };\n\n            return sse;\n\n        }\n    });\n\n\n    // Private Method for Handle EventSource object\n    function createEventSource(me) {\n        me.type = 'event';\n        me.instance = new EventSource(me._url);\n        me.instance.successCount = 0;\n\n        me.instance.onmessage = me._settings.onMessage;\n        me.instance.onopen = function (e) {\n            if (me.instance.successCount++ === 0) {\n                me._settings.onOpen(e);\n            }\n        };\n        me.instance.onerror = function (e) {\n            if (e.target.readyState === EventSource.CLOSED) {\n                me._settings.onError(e);\n            }\n        };\n\n        for (var key in me._settings.events) {\n            me.instance.addEventListener(key, me._settings.events[key], false);\n        }\n    }\n\n    // Handle the Ajax instance (fallback)\n    function createAjax(me) {\n        me.type = 'ajax';\n        me.instance = {successCount: 0, id: null, retry: 3000, data: \"\", event: \"\"};\n        runAjax(me);\n    }\n\n    // Handle the continous Ajax request (fallback)\n    function runAjax(me) {\n        if (!me.instance) {\n            return;\n        }\n\n        var headers = {'Last-Event-ID': me.instance.id};\n\n        $.extend(headers, me._settings.headers);\n\n        $.ajax({\n            url: me._url,\n            method: 'GET',\n            headers: headers,\n            success: function (receivedData, status, info) {\n                if (!me.instance) {\n                    return;\n                }\n\n                if (me.instance.successCount++ === 0) {\n                    me._settings.onOpen();\n                }\n\n                var lines = receivedData.split(\"\\n\");\n\n                // Process the return to generate a compatible SSE response\n                me.instance.data = \"\";\n                var countBreakLine = 0;\n                for (var key in lines) {\n                    var separatorPos = lines[key].indexOf(\":\");\n                    var item = [\n                        lines[key].substr(0, separatorPos),\n                        lines[key].substr(separatorPos + 1)\n                    ];\n                    switch (item[0]) {\n                        // If the first part is empty, needed to check another sequence\n                        case \"\":\n                            if (!item[1] && countBreakLine++ === 1) {  // Avoid comments!\n                                eventMessage = {\n                                    data: me.instance.data,\n                                    lastEventId: me.instance.id,\n                                    origin: 'http://' + info.getResponseHeader('Host'),\n                                    returnValue: true\n                                };\n\n                                // If there are a custom event then call it\n                                if (me.instance.event && me._settings.events[me.instance.event]) {\n                                    me._settings.events[me.instance.event](eventMessage);\n                                } else {\n                                    me._settings.onMessage(eventMessage);\n                                }\n                                me.instance.data = \"\";\n                                me.instance.event = \"\";\n                                countBreakLine = 0;\n                            }\n                            break;\n\n                        // Define the new retry object;\n                        case \"retry\":\n                            countBreakLine = 0;\n                            me.instance.retry = parseInt(item[1].trim());\n                            break;\n\n                        // Define the new ID\n                        case \"id\":\n                            countBreakLine = 0;\n                            me.instance.id = item[1].trim();\n                            break;\n\n                        // Define a custom event\n                        case \"event\":\n                            countBreakLine = 0;\n                            me.instance.event = item[1].trim();\n                            break;\n\n                        // Define the data to be processed.\n                        case \"data\":\n                            countBreakLine = 0;\n                            me.instance.data += (me.instance.data !== \"\" ? \"\\n\" : \"\") + item[1].trim();\n                            break;\n\n                        default:\n                            countBreakLine = 0;\n                    }\n                }\n                setTimeout(function () {\n                    runAjax(me);\n                }, me.instance.retry);\n            },\n            error: me._settings.onError\n        });\n    }\n\n})(jQuery);\n"
  },
  {
    "path": "springboot-basic/src/main/resources/static/js/samplessse.js",
    "content": "$( document ).ready(function() {\n\n    var sse = $.SSE('/kafka-messages', {\n        onMessage: function(e){\n            console.log(e);\n            $('#kafka-messages tr:last').after('<tr><td>'+e.data+'</td></tr>');\n        },\n        onError: function(e){\n            sse.stop();\n            console.log(\"Could not connect..Stopping SSE\");\n        },\n        onEnd: function(e){\n            console.log(\"End\");\n        }\n    });\n    sse.start();\n\n});"
  },
  {
    "path": "springboot-basic/src/main/resources/templates/fragments.html",
    "content": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head th:fragment=\"samples-header\">\n    <link rel=\"stylesheet\"\n          href=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css\"/>\n    <link rel=\"stylesheet\"\n          href=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/themes/prism.min.css\"/>\n    <link href=\"https://fonts.googleapis.com/css2?family=Lato:ital,wght@1,400;1,700&display=swap\" rel=\"stylesheet\">\n    <script src=\"https://code.jquery.com/jquery-3.3.1.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.11.0/umd/popper.min.js\"></script>\n    <script src=\"https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js\"></script>\n    <script src=\"https://cdnjs.cloudflare.com/ajax/libs/prism/9000.0.1/prism.min.js\"></script>\n</head>\n<body>\n<footer class=\"text-center text-lg-start bg-light text-muted\" th:fragment=\"samples-footer\">\n    <div class=\"me-5 d-none d-lg-block\">\n        <a href=\"/\"> Back to Samples </a>\n    </div>\n</footer>\n</body>\n</html>"
  },
  {
    "path": "springboot-basic/src/main/resources/templates/hello.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h4 class=\"card-title\" th:text=\"'Temporal Java SDK Samples: ' + ${sample}\">Temporal Java SDK Samples</h4>\n            <h6>This is a simple greeting example. Enter persons first and last name then submit form to start\n            workflow execution. Results will be updated on the page.</h6>\n            <div class=\"form-group\">\n                <br/><br/><br/>\n                <h5>Say hello to:</h5>\n                <form action=\"/hello\", id=\"sampleform\">\n                    <p>First Name: <input type=\"text\" name=\"firstName\"/></p>\n                    <p>Last Name: <input type=\"text\" name=\"lastName\"/></p>\n                    <p><input type=\"submit\" value=\"Run Workflow\" class=\"btn btn-primary\" />\n                        <input type=\"reset\" value=\"Reset Form\" class=\"btn btn-secondary\" />\n                </form>\n            </div>\n        </div>\n        <div style=\"width: 18rem;\">\n            <div>\n                <h5 class=\"card-title\">Workflow result:</h5>\n                <div id=\"result\"></div>\n            </div>\n        </div>\n    </div>\n</div>\n<script>\n$(\"#sampleform\").submit(function( event ) {\n    event.preventDefault();\n\n    var $form = $( this ),\n        firstName = $form.find( \"input[name='firstName']\" ).val(),\n        lastName = $form.find( \"input[name='lastName']\" ).val(),\n        url = $form.attr( \"action\" );\n\n    $.ajax({\n        'url': url,\n        'method':'POST',\n        'dataType': 'json',\n        'contentType': 'application/json',\n        'data':JSON.stringify({\n            \"firstName\": firstName,\n            \"lastName\": lastName\n        }),\n        success: function(response) {\n            $( \"#result\" ).empty().append( response );\n        }\n    });\n});\n</script>\n<footer th:replace=\"fragments :: samples-footer\"></footer>\n</body>\n</html>"
  },
  {
    "path": "springboot-basic/src/main/resources/templates/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\" xmlns:th=\"http://www.thymeleaf.org\">\n<head th:replace=\"fragments :: samples-header\"></head>\n<body>\n<div class=\"container\">\n    <div class=\"card\">\n        <div class=\"card-body\">\n            <h5 class=\"card-title\">Temporal Java SDK Samples</h5>\n            <p class=\"card-text\">Click on sample link to run it.</p>\n            <div class=\"list-group\">\n                <a href=\"/hello\" class=\"list-group-item list-group-item-action\">Say Hello</a>\n            </div>\n        </div>\n    </div>\n</div>\n</body>\n</html>"
  },
  {
    "path": "springboot-basic/src/test/java/io/temporal/samples/springboot/HelloSampleTest.java",
    "content": "package io.temporal.samples.springboot;\n\nimport io.temporal.client.WorkflowClient;\nimport io.temporal.client.WorkflowOptions;\nimport io.temporal.samples.springboot.hello.HelloWorkflow;\nimport io.temporal.samples.springboot.hello.model.Person;\nimport io.temporal.testing.TestWorkflowEnvironment;\nimport org.junit.jupiter.api.BeforeEach;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.TestInstance;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.context.ConfigurableApplicationContext;\nimport org.springframework.context.annotation.ComponentScan;\nimport org.springframework.test.annotation.DirtiesContext;\nimport org.springframework.test.context.ActiveProfiles;\nimport org.springframework.util.Assert;\n\n@SpringBootTest(classes = HelloSampleTest.Configuration.class)\n@ActiveProfiles(\"test\")\n@TestInstance(TestInstance.Lifecycle.PER_CLASS)\n@DirtiesContext\npublic class HelloSampleTest {\n\n  @Autowired ConfigurableApplicationContext applicationContext;\n\n  @Autowired TestWorkflowEnvironment testWorkflowEnvironment;\n\n  @Autowired WorkflowClient workflowClient;\n\n  @BeforeEach\n  void setUp() {\n    applicationContext.start();\n  }\n\n  @Test\n  public void testHello() {\n    HelloWorkflow workflow =\n        workflowClient.newWorkflowStub(\n            HelloWorkflow.class,\n            WorkflowOptions.newBuilder()\n                .setTaskQueue(\"HelloSampleTaskQueue\")\n                .setWorkflowId(\"HelloSampleTest\")\n                .build());\n    String result = workflow.sayHello(new Person(\"Temporal\", \"User\"));\n    Assert.notNull(result, \"Greeting should not be null\");\n    Assert.isTrue(result.equals(\"Hello Temporal User!\"), \"Invalid result\");\n  }\n\n  @ComponentScan\n  public static class Configuration {}\n}\n"
  },
  {
    "path": "springboot-basic/src/test/resources/application.yaml",
    "content": "server:\n  port: 3030\nspring:\n  main:\n    allow-bean-definition-overriding: true\n  application:\n    name: temporal-samples\n  # temporal specific configs\n  temporal:\n    connection:\n      target: 127.0.0.1:7233\n      target.namespace: default\n    workersAutoDiscovery:\n      packages: io.temporal.samples.springboot\n    # enable test server for testing\n    test-server:\n      enabled: true\n# specific for samples\nsamples:\n  data:\n    language: english\n"
  }
]