Skip to content

Commit 22877a1

Browse files
authored
[Fix #1267] Adapting to GraalVM static grabbing policy (#1268)
Signed-off-by: fjtirado <ftirados@redhat.com>
1 parent a98675c commit 22877a1

6 files changed

Lines changed: 140 additions & 112 deletions

File tree

impl/container/src/main/java/io/serverlessworkflow/impl/container/executors/ContainerRunner.java

Lines changed: 67 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -41,21 +41,11 @@
4141
import java.util.Optional;
4242
import java.util.concurrent.CompletableFuture;
4343
import java.util.concurrent.TimeUnit;
44+
import java.util.concurrent.locks.Lock;
45+
import java.util.concurrent.locks.ReentrantLock;
4446

4547
class ContainerRunner implements CallableTask {
4648

47-
private static final DefaultDockerClientConfig DEFAULT_CONFIG =
48-
DefaultDockerClientConfig.createDefaultConfigBuilder().build();
49-
50-
private static class DockerClientHolder {
51-
private static final DockerClient dockerClient =
52-
DockerClientImpl.getInstance(
53-
DEFAULT_CONFIG,
54-
new ApacheDockerHttpClient.Builder()
55-
.dockerHost(DEFAULT_CONFIG.getDockerHost())
56-
.build());
57-
}
58-
5949
private final Collection<ContainerPropertySetter> propertySetters;
6050
private final Optional<WorkflowValueResolver<Duration>> timeout;
6151
private final ContainerCleanupPolicy policy;
@@ -82,7 +72,8 @@ public CompletableFuture<WorkflowModel> apply(
8272

8373
private WorkflowModel startSync(
8474
WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) {
85-
Integer exit = executeContainer(workflowContext, taskContext, input);
75+
Integer exit =
76+
executeContainer(workflowContext, taskContext, input, DockerClientHolder.client());
8677
if (exit == null || exit == 0) {
8778
return input;
8879
} else {
@@ -91,14 +82,20 @@ private WorkflowModel startSync(
9182
}
9283

9384
private Integer executeContainer(
94-
WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input) {
85+
WorkflowContext workflowContext,
86+
TaskContext taskContext,
87+
WorkflowModel input,
88+
DockerClient dockerClient) {
9589
try {
96-
pullImageIfNeeded(containerImage);
97-
CreateContainerCmd containerCommand =
98-
DockerClientHolder.dockerClient.createContainerCmd(containerImage);
90+
pullImageIfNeeded(containerImage, dockerClient);
91+
CreateContainerCmd containerCommand = dockerClient.createContainerCmd(containerImage);
9992
propertySetters.forEach(p -> p.accept(containerCommand, workflowContext, taskContext, input));
10093
return waitAccordingToLifetime(
101-
createAndStartContainer(containerCommand), workflowContext, taskContext, input);
94+
createAndStartContainer(containerCommand, dockerClient),
95+
workflowContext,
96+
taskContext,
97+
input,
98+
dockerClient);
10299
} catch (InterruptedException ie) {
103100
Thread.currentThread().interrupt();
104101
throw failed("Container execution failed with exit code " + ie.getMessage());
@@ -107,43 +104,46 @@ private Integer executeContainer(
107104
}
108105
}
109106

110-
private void pullImageIfNeeded(String imageRef) throws InterruptedException {
107+
private void pullImageIfNeeded(String imageRef, DockerClient dockerClient)
108+
throws InterruptedException {
111109
NameParser.ReposTag rt = NameParser.parseRepositoryTag(imageRef);
112-
DockerClientHolder.dockerClient
110+
dockerClient
113111
.pullImageCmd(NameParser.resolveRepositoryName(imageRef).reposName)
114112
.withTag(WorkflowUtils.isValid(rt.tag) ? rt.tag : "latest")
115113
.start()
116114
.awaitCompletion();
117115
}
118116

119-
private String createAndStartContainer(CreateContainerCmd containerCommand) {
117+
private String createAndStartContainer(
118+
CreateContainerCmd containerCommand, DockerClient dockerClient) {
120119
CreateContainerResponse resp = containerCommand.exec();
121120
String id = resp.getId();
122121
if (!isValid(id)) {
123122
throw new IllegalStateException("Container creation failed: empty ID");
124123
}
125-
DockerClientHolder.dockerClient.startContainerCmd(id).exec();
124+
dockerClient.startContainerCmd(id).exec();
126125
return id;
127126
}
128127

129128
private Integer waitAccordingToLifetime(
130-
String id, WorkflowContext workflowContext, TaskContext taskContext, WorkflowModel input)
129+
String id,
130+
WorkflowContext workflowContext,
131+
TaskContext taskContext,
132+
WorkflowModel input,
133+
DockerClient dockerClient)
131134
throws IOException {
132-
try (var cb =
133-
DockerClientHolder.dockerClient
134-
.waitContainerCmd(id)
135-
.exec(new WaitContainerResultCallback())) {
135+
try (var cb = dockerClient.waitContainerCmd(id).exec(new WaitContainerResultCallback())) {
136136
if (policy == ContainerCleanupPolicy.EVENTUALLY) {
137137
Duration timeout =
138138
this.timeout
139139
.map(t -> t.apply(workflowContext, taskContext, input))
140140
.orElse(Duration.ZERO);
141141
try {
142142
Integer exit = cb.awaitStatusCode(timeout.toMillis(), TimeUnit.MILLISECONDS);
143-
safeStop(id);
143+
safeStop(id, dockerClient);
144144
return exit;
145145
} catch (DockerClientException timeoutOrOther) {
146-
safeStop(id);
146+
safeStop(id, dockerClient);
147147
}
148148
} else {
149149
return cb.awaitStatusCode();
@@ -154,47 +154,41 @@ private Integer waitAccordingToLifetime(
154154
return 0;
155155
}
156156

157-
private boolean isRunning(String id) {
157+
private boolean isRunning(String id, DockerClient dockerClient) {
158158
try {
159-
var st = DockerClientHolder.dockerClient.inspectContainerCmd(id).exec().getState();
159+
var st = dockerClient.inspectContainerCmd(id).exec().getState();
160160
return st != null && Boolean.TRUE.equals(st.getRunning());
161161
} catch (Exception e) {
162162
return false; // must be already removed
163163
}
164164
}
165165

166-
private void safeStop(String id) {
167-
if (isRunning(id)) {
168-
safeStop(id, Duration.ofSeconds(10));
169-
try (var cb2 =
170-
DockerClientHolder.dockerClient
171-
.waitContainerCmd(id)
172-
.exec(new WaitContainerResultCallback())) {
166+
private void safeStop(String id, DockerClient dockerClient) {
167+
if (isRunning(id, dockerClient)) {
168+
safeStop(id, Duration.ofSeconds(10), dockerClient);
169+
try (var cb2 = dockerClient.waitContainerCmd(id).exec(new WaitContainerResultCallback())) {
173170
cb2.awaitStatusCode();
174-
safeRemove(id);
171+
safeRemove(id, dockerClient);
175172
} catch (Exception ignore) {
176173
// we can ignore this
177174
}
178175
} else {
179-
safeRemove(id);
176+
safeRemove(id, dockerClient);
180177
}
181178
}
182179

183-
private void safeStop(String id, Duration timeout) {
180+
private void safeStop(String id, Duration timeout, DockerClient dockerClient) {
184181
try {
185-
DockerClientHolder.dockerClient
186-
.stopContainerCmd(id)
187-
.withTimeout((int) Math.max(1, timeout.toSeconds()))
188-
.exec();
182+
dockerClient.stopContainerCmd(id).withTimeout((int) Math.max(1, timeout.toSeconds())).exec();
189183
} catch (Exception ignore) {
190184
// we can ignore this
191185
}
192186
}
193187

194188
// must be removed because of withAutoRemove(true), but just in case
195-
private void safeRemove(String id) {
189+
private void safeRemove(String id, DockerClient dockerClient) {
196190
try {
197-
DockerClientHolder.dockerClient.removeContainerCmd(id).withForce(true).exec();
191+
dockerClient.removeContainerCmd(id).withForce(true).exec();
198192
} catch (Exception ignore) {
199193
// we can ignore this
200194
}
@@ -217,4 +211,30 @@ private static RuntimeException mapExitCode(int exit) {
217211
private static RuntimeException failed(String message) {
218212
return new RuntimeException(message);
219213
}
214+
215+
private static class DockerClientHolder {
216+
private static volatile DockerClient client;
217+
private static final Lock dockerLock = new ReentrantLock();
218+
219+
private static DockerClient client() {
220+
if (client == null) {
221+
dockerLock.lock();
222+
try {
223+
if (client == null) {
224+
DefaultDockerClientConfig config =
225+
DefaultDockerClientConfig.createDefaultConfigBuilder().build();
226+
client =
227+
DockerClientImpl.getInstance(
228+
config,
229+
new ApacheDockerHttpClient.Builder()
230+
.dockerHost(config.getDockerHost())
231+
.build());
232+
}
233+
} finally {
234+
dockerLock.unlock();
235+
}
236+
}
237+
return client;
238+
}
239+
}
220240
}

impl/core/src/main/java/io/serverlessworkflow/impl/auth/AuthUtils.java

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@
1515
*/
1616
package io.serverlessworkflow.impl.auth;
1717

18-
import java.util.Random;
19-
2018
public class AuthUtils {
2119

2220
private AuthUtils() {}
@@ -40,15 +38,7 @@ private AuthUtils() {}
4038

4139
private static final String AUTH_HEADER_FORMAT = "%s %s";
4240

43-
private static class RandomHolder {
44-
private static final Random random = new Random();
45-
}
46-
4741
public static String authHeaderValue(String scheme, String parameter) {
4842
return String.format(AUTH_HEADER_FORMAT, scheme, parameter);
4943
}
50-
51-
public static String getRandomHexString() {
52-
return String.format("%08x", RandomHolder.random.nextInt());
53-
}
5444
}

impl/core/src/main/java/io/serverlessworkflow/impl/auth/DigestAuthProvider.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@
2929
import io.serverlessworkflow.impl.WorkflowModel;
3030
import io.serverlessworkflow.impl.WorkflowUtils;
3131
import io.serverlessworkflow.impl.WorkflowValueResolver;
32+
import io.serverlessworkflow.impl.utils.RandomFactory;
3233
import java.io.IOException;
3334
import java.io.UncheckedIOException;
3435
import java.net.HttpURLConnection;
3536
import java.net.URI;
3637
import java.security.MessageDigest;
3738
import java.security.NoSuchAlgorithmException;
3839
import java.util.Optional;
40+
import java.util.Random;
3941
import java.util.StringTokenizer;
4042
import java.util.concurrent.atomic.AtomicInteger;
4143
import java.util.regex.Matcher;
@@ -110,9 +112,11 @@ private static enum QOP {
110112
AUTH_INT,
111113
};
112114

115+
private static final String HEX = "%08x";
113116
private final WorkflowValueResolver<String> userFilter;
114117
private final WorkflowValueResolver<String> passwordFilter;
115118
private final String method;
119+
private final Random random = RandomFactory.getRandom();
116120

117121
public DigestAuthProvider(
118122
WorkflowApplication app,
@@ -155,8 +159,8 @@ public String content(WorkflowContext workflow, TaskContext task, WorkflowModel
155159
String nonceCount;
156160
String clientNonce;
157161
if (serverInfo.qop.isPresent() || serverInfo.algorithm == Algorithm.MD5SESSS) {
158-
nonceCount = String.format("%08x", nc.getAndIncrement());
159-
clientNonce = AuthUtils.getRandomHexString();
162+
nonceCount = String.format(HEX, nc.getAndIncrement());
163+
clientNonce = String.format(HEX, random.nextInt());
160164
} else {
161165
nonceCount = null;
162166
clientNonce = null;
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2020-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package io.serverlessworkflow.impl.utils;
17+
18+
import java.util.Random;
19+
20+
public class RandomFactory {
21+
22+
public static Random getRandom() {
23+
return new Random();
24+
}
25+
26+
private RandomFactory() {}
27+
}

impl/http/src/main/java/io/serverlessworkflow/impl/executors/http/HttpClientResolver.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,37 @@
2020
import io.serverlessworkflow.impl.WorkflowContext;
2121
import jakarta.ws.rs.client.Client;
2222
import jakarta.ws.rs.client.ClientBuilder;
23+
import java.util.concurrent.locks.Lock;
24+
import java.util.concurrent.locks.ReentrantLock;
2325

2426
public class HttpClientResolver {
2527

2628
public static final String HTTP_CLIENT_PROVIDER = "httpClientProvider";
2729

2830
private static class DefaultHolder {
29-
private static final Client client = ClientBuilder.newClient();
31+
32+
private static final Lock clientLock = new ReentrantLock();
33+
private static volatile Client client;
34+
35+
private static final Client client() {
36+
if (client == null) {
37+
clientLock.lock();
38+
try {
39+
if (client == null) {
40+
client = ClientBuilder.newClient();
41+
}
42+
} finally {
43+
clientLock.unlock();
44+
}
45+
}
46+
return client;
47+
}
3048
}
3149

3250
public static Client client(WorkflowContext workflowContext, TaskContext taskContext) {
3351
WorkflowApplication appl = workflowContext.definition().application();
3452
return appl.<Client>additionalObject(HTTP_CLIENT_PROVIDER, workflowContext, taskContext)
35-
.orElseGet(() -> DefaultHolder.client);
53+
.orElseGet(() -> DefaultHolder.client());
3654
}
3755

3856
private HttpClientResolver() {}

0 commit comments

Comments
 (0)