Skip to content
Open
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
4 changes: 4 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,10 @@
<artifactId>micrometer-core</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>context-propagation</artifactId>
</dependency>

<!-- Powsybl -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

import com.powsybl.computation.ComputationManager;
import com.powsybl.computation.local.LocalComputationManager;
import io.micrometer.context.ContextExecutorService;
import io.micrometer.context.ContextSnapshotFactory;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import lombok.Getter;
Expand All @@ -33,7 +35,8 @@ public class ExecutionService {
@SneakyThrows
@PostConstruct
private void postConstruct() {
executorService = Executors.newCachedThreadPool();
executorService = ContextExecutorService.wrap(Executors.newCachedThreadPool(),
() -> ContextSnapshotFactory.builder().build().captureAll());
computationManager = new LocalComputationManager(getExecutorService());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright (c) 2025, RTE (http://www.rte-france.com)
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.gridsuite.computation.service;

import io.micrometer.context.ContextExecutorService;
import io.micrometer.context.ContextRegistry;
import io.micrometer.context.ThreadLocalAccessor;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.concurrent.ExecutorService;

import static org.junit.jupiter.api.Assertions.*;

/**
* @author Mohamed Benrejeb <mohamed.ben-rejeb at rte-france.com>
*/
class ExecutionServiceTest {

private static final String THREAD_LOCAL_KEY = "computation-thread-local";
private final ThreadLocal<String> threadLocal = new ThreadLocal<>();

@AfterEach
void tearDown() {
ContextRegistry.getInstance().removeThreadLocalAccessor(THREAD_LOCAL_KEY);
threadLocal.remove();
}

@Test
void postConstructWrapsExecutorAndPropagatesContext() throws Exception {
ContextRegistry.getInstance().registerThreadLocalAccessor(new ThreadLocalAccessor<String>() {
@Override
public String key() {
return THREAD_LOCAL_KEY;
}

@Override
public String getValue() {
return threadLocal.get();
}

@Override
public void setValue(String value) {
threadLocal.set(value);
}

@Override
public void setValue() {
threadLocal.remove();
}
});

ExecutionService service = new ExecutionService();
Method postConstruct = ExecutionService.class.getDeclaredMethod("postConstruct");
postConstruct.setAccessible(true);
postConstruct.invoke(service);

Field executorField = ExecutionService.class.getDeclaredField("executorService");
executorField.setAccessible(true);

ExecutorService executorService = (ExecutorService) executorField.get(service);
assertInstanceOf(ContextExecutorService.class, executorService, "executor should be wrapped in ContextExecutorService");

threadLocal.set("expected-context");
assertEquals("expected-context", executorService.submit(threadLocal::get).get());
assertNotNull(service.getComputationManager());

}
}