This is an automated email from the ASF dual-hosted git repository. rzo1 pushed a commit to branch tomee-10.x in repository https://gitbox.apache.org/repos/asf/tomee.git
commit 5529874b6f45eafa8faae2282d0cb15a638d17c9 Author: Richard Zowalla <[email protected]> AuthorDate: Fri Sep 25 20:59:16 2026 +0200 TOMEE-4011 generate module ids atomically so parallel deployments don't collide (cherry picked from commit 15a380d5c5d0ae7fdbf7f0b0ea895125a50b4b0e) --- .../java/org/apache/openejb/config/Module.java | 5 +- .../apache/openejb/config/ModuleUniqueIdTest.java | 63 ++++++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/container/openejb-core/src/main/java/org/apache/openejb/config/Module.java b/container/openejb-core/src/main/java/org/apache/openejb/config/Module.java index f260ad965e..54bd874ca5 100644 --- a/container/openejb-core/src/main/java/org/apache/openejb/config/Module.java +++ b/container/openejb-core/src/main/java/org/apache/openejb/config/Module.java @@ -26,10 +26,11 @@ import java.util.HashSet; import java.util.Map; import java.util.Properties; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; public class Module { // note: 0 is reserved for internal app, don't set it to 0! - private static int currentId = 1; // unique id to be able to bind something for each module in the jndi tree + private static final AtomicInteger CURRENT_ID = new AtomicInteger(1); // unique id to be able to bind something for each module in the jndi tree private boolean isStandaloneModule = true; @@ -45,7 +46,7 @@ public class Module { public Module(final boolean needId) { if (needId) { - uniqueId = Integer.toString(currentId++); + uniqueId = Integer.toString(CURRENT_ID.getAndIncrement()); } } diff --git a/container/openejb-core/src/test/java/org/apache/openejb/config/ModuleUniqueIdTest.java b/container/openejb-core/src/test/java/org/apache/openejb/config/ModuleUniqueIdTest.java new file mode 100644 index 0000000000..8ece98840d --- /dev/null +++ b/container/openejb-core/src/test/java/org/apache/openejb/config/ModuleUniqueIdTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.openejb.config; + +import org.junit.Test; + +import java.util.List; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class ModuleUniqueIdTest { + @Test + public void uniqueIdsWhenCreatedConcurrently() throws Exception { + final int threads = 16; + final int perThread = 500; + final Set<String> ids = ConcurrentHashMap.newKeySet(); + final CountDownLatch start = new CountDownLatch(1); + final ExecutorService pool = Executors.newFixedThreadPool(threads); + try { + final List<Future<?>> futures = IntStream.range(0, threads) + .mapToObj(i -> pool.submit(() -> { + start.await(); + for (int j = 0; j < perThread; j++) { + ids.add(new Module(true).getUniqueId()); + } + return null; + })) + .collect(Collectors.toList()); + start.countDown(); + for (final Future<?> future : futures) { + future.get(1, TimeUnit.MINUTES); + } + } finally { + pool.shutdownNow(); + assertTrue(pool.awaitTermination(1, TimeUnit.MINUTES)); + } + assertEquals(threads * perThread, ids.size()); + } +}
