Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 11 additions & 7 deletions .github/workflows/prepare-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,12 @@ jobs:
- runner: macos-13
os_family: macOS
arch: amd64
- runner: macos-latest
os_family: macOS
arch: arm64
- runner: ubuntu-24.04-arm
os_family: linux
arch: arm64
- runner: windows-2019
os_family: windows
arch: amd64
Expand All @@ -154,8 +160,8 @@ jobs:
if: matrix.os_family != 'Linux'
uses: actions/setup-java@v4
with:
java-version: "11"
distribution: "temurin"
java-version: "21"
distribution: "graalvm"

- name: Set up Gradle
if: matrix.os_family != 'Linux'
Expand All @@ -164,24 +170,22 @@ jobs:
- name: Build native test server (non-Docker)
if: matrix.os_family != 'Linux'
run: |
./gradlew :temporal-test-server:build
./gradlew -PnativeBuild :temporal-test-server:nativeBuild

- name: Build native test server (Docker)
if: matrix.os_family == 'Linux'
run: |
docker run \
--rm -w /github/workspace -v "$(pwd):/github/workspace" \
$(docker build -q ./docker/native-image) \
sh -c "./gradlew :temporal-test-server:build"
sh -c "./gradlew -PnativeBuild :temporal-test-server:nativeBuild"
# path ends in a wildcard because on windows the file ends in '.exe'
# path excludes *.txt because native-image also writes a build manifest txt file
- name: Upload executable to workflow
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.os_family }}_${{ matrix.arch }}
path: |
temporal-test-server/build/graal/temporal-test-server*
!temporal-test-server/build/graal/*.txt
temporal-test-server/build/native/nativeCompile/temporal-test-server*
Comment on lines -183 to +188
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can I get confirmation that we believe the release artifact files will have the same name, just with arm ones now? So temporal-test-server_<version>_linux_arm64.tar.gz and temporal-test-server_<version>_maxOS_arm64.tar.gz will now be present?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep

if-no-files-found: error
retention-days: 1

Expand Down
4 changes: 2 additions & 2 deletions docker/native-image/dockerfile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Use an old version of Ubuntu to build the test server to maintain compatibility with
# older versions of glibc, specifically glib 2.17.
FROM ubuntu:18.04
ENV JAVA_HOME=/opt/java/openjdk
COPY --from=eclipse-temurin:21 $JAVA_HOME $JAVA_HOME
ENV JAVA_HOME=/usr/lib64/graalvm/graalvm-community-java21
COPY --from=ghcr.io/graalvm/jdk-community:21 $JAVA_HOME $JAVA_HOME
ENV PATH="${JAVA_HOME}/bin:${PATH}"
RUN apt-get update
RUN apt-get install -y git build-essential zlib1g-dev
Expand Down
8 changes: 4 additions & 4 deletions gradle/java.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ subprojects {

java {
// graal only supports java 8, 11, 16, 17, 21, 23
sourceCompatibility = project.hasProperty("edgeDepsTest") ? JavaVersion.VERSION_21 : JavaVersion.VERSION_1_8
targetCompatibility = project.hasProperty("edgeDepsTest") ? JavaVersion.VERSION_21 : JavaVersion.VERSION_1_8
sourceCompatibility = project.hasProperty("edgeDepsTest") || project.hasProperty("nativeBuild") ? JavaVersion.VERSION_21 : JavaVersion.VERSION_1_8
targetCompatibility = project.hasProperty("edgeDepsTest") || project.hasProperty("nativeBuild") ? JavaVersion.VERSION_21 : JavaVersion.VERSION_1_8
withJavadocJar()
withSourcesJar()
}

compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << '-Xlint:none' << '-Xlint:deprecation' << '-Werror' << '-parameters'
if (JavaVersion.current().isJava9Compatible() && !project.hasProperty("edgeDepsTest")) {
if (JavaVersion.current().isJava9Compatible() && !project.hasProperty("edgeDepsTest") && !project.hasProperty("nativeBuild")) {
// https://stackoverflow.com/a/43103115/525203
options.compilerArgs.addAll(['--release', '8'])
}
Expand All @@ -24,7 +24,7 @@ subprojects {
compileTestJava {
options.encoding = 'UTF-8'
options.compilerArgs << '-Xlint:none' << '-Xlint:deprecation' << '-Werror' << '-parameters'
if (JavaVersion.current().isJava9Compatible() && !project.hasProperty("edgeDepsTest")) {
if (JavaVersion.current().isJava9Compatible() && !project.hasProperty("edgeDepsTest") && !project.hasProperty("nativeBuild")) {
// https://stackoverflow.com/a/43103115/525203
options.compilerArgs.addAll(['--release', '8'])
}
Expand Down
25 changes: 16 additions & 9 deletions temporal-test-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,35 @@ This service allows to run a test-only in-memory implementation of Temporal serv
## To build a test server using GraalVM native-image

From the root of the java-sdk repo:
```
./gradlew :temporal-test-server:build
```bash
./gradlew -PnativeBuild :temporal-test-server:nativeCompile
```
This will give you a native executable `build/graal/temporal-test-server`. The
executable requires a single argument: the port number on which it should
listen.

## To build a test server docker image
## To run the test server as a native-image through Gradle

From the root of the java-sdk repo:
```
./gradlew :temporal-test-server:docker
```bash
./gradlew -PnativeBuild :temporal-test-server:nativeRun
```

This will result in a local image being built:
`temporalio/temporal-test-server`.

## GraalVM native-image configuration

The GraalVM native-image compiler uses the native-image.properties file and the
referenced JSON files during compilation. The JSON files are generated by
running the test server java code in a JVM configured with the [GraalVM tracing
agent](https://www.graalvm.org/latest/reference-manual/native-image/metadata/AutomaticMetadataCollection/) configured,
e.g. with the flag
`-agentlib:native-image-agent=config-output-dir=temporal-test-server/src/main/resources/META-INF/native-image/io.temporal/temporal-test-server`.

### To run the test server test with tracing agent through Gradle

```bash
./gradlew -Pagent -PnativeBuild :temporal-test-server:test
```

### Copy metadata from test run
```bash
./gradlew -PnativeBuild :temporal-test-server:metadataCopy --task test
```
68 changes: 38 additions & 30 deletions temporal-test-server/build.gradle
Original file line number Diff line number Diff line change
@@ -1,13 +1,6 @@
buildscript {
ext {
// 0.11.0 and later are build on JDK 11 bytecode version
graalVersion = "${JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_11) ? '0.12.0' : '0.10.0'}"
}
}

plugins {
id 'application'
id 'com.palantir.graal' version "${graalVersion}"
id 'org.graalvm.buildtools.native' version '0.10.6' apply false
id 'com.google.protobuf' version '0.9.2'
}

Expand Down Expand Up @@ -99,28 +92,43 @@ idea {
}


graal {
outputName "temporal-test-server"
mainClass application.getMainClass().get()
javaVersion '17'
graalVersion '22.3.1'

// Don't fallback to running a JVM
option "--no-fallback"

// Signal handling so that ^C actually stops the process
option "--install-exit-handlers"

// If we're on linux, static link everything but libc. Otherwise link
// everything dynamically (note the '-' rather than '+' in fromt of
// StaticExecutable)
option isLinux()
? "-H:+StaticExecutableWithDynamicLibC"
: "-H:-StaticExecutable"
}

def isLinux() {
return System.properties['os.name'].toLowerCase().contains('linux')
return System.properties['os.name'].toLowerCase().contains('linux')
}

tasks.build.dependsOn('nativeImage')
// The graalvm plugin requires we build with Java 11
if (project.hasProperty("nativeBuild")) {
apply plugin: 'org.graalvm.buildtools.native'

graalvmNative {
toolchainDetection = true
agent {
enabled = true
defaultMode = "standard"
metadataCopy {
outputDirectories.add("src/main/resources/META-INF/native-image/io.temporal/temporal-test-server")
mergeWithExisting = false
}

}
binaries {
main {
mainClass = application.getMainClass().get()
sharedLibrary = false
// Signal handling so that ^C actually stops the process
buildArgs.add("--install-exit-handlers")
// If we're on linux, static link everything but libc. Otherwise link
// everything dynamically (note the '-' rather than '+' in front of
// StaticExecutable)
buildArgs.add(isLinux() ? "-H:+StaticExecutableWithDynamicLibC": "-H:-StaticExecutable")
Comment on lines +120 to +123
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect this may change a tad when we do #2402, specifically that I guess we'll add --libc=musl instead of dynamic libc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

buildArgs.add("-H:+UnlockExperimentalVMOptions")
buildArgs.add("-O4")

runtimeArgs.add("7233")
Copy link
Member

@cretz cretz Apr 2, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help me understand this a bit? Does it mean that this is the default arg if not provided? https://graalvm.github.io/native-build-tools/latest/gradle-plugin.html is not very clear to me.

(this is my only blocking concern/comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It sets the arg for ./gradlew -Pagent -PnativeBuild :temporal-test-server:nativeRun since there isn't another way I could find

Copy link
Member

@cretz cretz Apr 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can you confirm this has no affect on the final binary and that the final binary still requires a port and uses that port?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct

}
}
binaries.all {
verbose = true
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,22 @@
},
{
"name":"java.lang.String",
"methods":[
{"name":"lastIndexOf","parameterTypes":["int"] },
{"name":"substring","parameterTypes":["int"] }
]
"methods":[{"name":"lastIndexOf","parameterTypes":["int"] }, {"name":"substring","parameterTypes":["int"] }]
},
{
"name":"java.lang.System",
"methods":[
{"name":"getProperty","parameterTypes":["java.lang.String"] },
{"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }
]
"methods":[{"name":"getProperty","parameterTypes":["java.lang.String"] }, {"name":"setProperty","parameterTypes":["java.lang.String","java.lang.String"] }]
},
{
"name":"sun.instrument.InstrumentationImpl",
"methods":[{"name":"<init>","parameterTypes":["long","boolean","boolean","boolean"] }, {"name":"loadClassAndCallAgentmain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"loadClassAndCallPremain","parameterTypes":["java.lang.String","java.lang.String"] }, {"name":"transform","parameterTypes":["java.lang.Module","java.lang.ClassLoader","java.lang.String","java.lang.Class","java.security.ProtectionDomain","byte[]","boolean"] }]
},
{
"name":"sun.management.VMManagementImpl",
"fields":[
{"name":"compTimeMonitoringSupport"},
{"name":"currentThreadCpuTimeSupport"},
{"name":"objectMonitorUsageSupport"},
{"name":"otherThreadCpuTimeSupport"},
{"name":"remoteDiagnosticCommandsSupport"},
{"name":"synchronizerUsageSupport"},
{"name":"threadAllocatedMemorySupport"},
{"name":"threadContentionMonitoringSupport"}
]
"fields":[{"name":"compTimeMonitoringSupport"}, {"name":"currentThreadCpuTimeSupport"}, {"name":"objectMonitorUsageSupport"}, {"name":"otherThreadCpuTimeSupport"}, {"name":"remoteDiagnosticCommandsSupport"}, {"name":"synchronizerUsageSupport"}, {"name":"threadAllocatedMemorySupport"}, {"name":"threadContentionMonitoringSupport"}]
},
{
"name":"worker.org.gradle.process.internal.worker.GradleWorkerMain",
"methods":[{"name":"main","parameterTypes":["java.lang.String[]"] }]
}
]
]
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
# limitations under the License.
#

Args = -H:DynamicProxyConfigurationResources=${.}/proxy-config.json \
Args = -H:+UnlockExperimentalVMOptions \
-H:DynamicProxyConfigurationResources=${.}/proxy-config.json \
-H:JNIConfigurationResources=${.}/jni-config.json \
-H:ReflectionConfigurationResources=${.}/reflect-config.json \
-H:ResourceConfigurationResources=${.}/resource-config.json \
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,3 @@
]
}
]

Original file line number Diff line number Diff line change
@@ -1,2 +1,113 @@
[
]
{
"interfaces":["io.temporal.client.WorkflowClient"]
},
{
"interfaces":["io.temporal.serviceclient.OperatorServiceStubs"]
},
{
"interfaces":["io.temporal.serviceclient.TestServiceStubs"]
},
{
"interfaces":["io.temporal.serviceclient.WorkflowServiceStubs"]
},
{
"interfaces":["io.temporal.testserver.functional.DescribeWorkflowExecutionTest$TestDescribeActivity","io.temporal.internal.sync.AsyncInternal$AsyncMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.DescribeWorkflowExecutionTest$TestDescribeWorkflow","io.temporal.internal.sync.StubMarker","io.temporal.internal.sync.AsyncInternal$AsyncMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestActivities$ActivityReturnsString","io.temporal.internal.sync.AsyncInternal$AsyncMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$PrimitiveChildWorkflow","io.temporal.internal.sync.StubMarker","io.temporal.internal.sync.AsyncInternal$AsyncMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$PrimitiveWorkflow","io.temporal.internal.sync.StubMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$WorkflowReturnsString","io.temporal.internal.sync.StubMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$WorkflowTakesBool","io.temporal.internal.sync.StubMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$WorkflowWithSignal","io.temporal.internal.sync.StubMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.common.TestWorkflows$WorkflowWithUpdate","io.temporal.internal.sync.StubMarker"]
},
{
"interfaces":["io.temporal.testserver.functional.timeskipping.SleepingActivity","io.temporal.internal.sync.AsyncInternal$AsyncMarker"]
},
{
"interfaces":["net.bytebuddy.description.method.MethodDescription$InDefinedShape$AbstractBase$Executable"]
},
{
"interfaces":["net.bytebuddy.description.method.ParameterDescription$ForLoadedParameter$Parameter"]
},
{
"interfaces":["net.bytebuddy.description.method.ParameterList$ForLoadedExecutable$Executable"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDefinition$Sort$AnnotatedType"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$ForLoadedType$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableExceptionType$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedExecutableParameterType$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedField$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$Delegator$ForLoadedMethodReturnType$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForComponentType$AnnotatedParameterizedType"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForTypeArgument$AnnotatedParameterizedType"]
},
{
"interfaces":["net.bytebuddy.description.type.TypeDescription$Generic$AnnotationReader$ForWildcardUpperBoundType$AnnotatedWildcardType"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfClassDesc"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDirectMethodHandleDesc$ForKind"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfDynamicConstantDesc"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodHandleDesc"]
},
{
"interfaces":["net.bytebuddy.utility.JavaConstant$Simple$Dispatcher$OfMethodTypeDesc"]
},
{
"interfaces":["net.bytebuddy.utility.JavaModule$Module"]
},
{
"interfaces":["net.bytebuddy.utility.JavaModule$Resolver"]
}
]
Loading
Loading