From adef8351bd6b9b0cba9ce19fb8414807bd93a006 Mon Sep 17 00:00:00 2001 From: Andrei Solntsev Date: Sat, 28 Mar 2026 18:30:15 +0200 Subject: [PATCH] synchronize access to IdentityHashMap instances ... to avoid concurrency issues like in build https://github.com/datafaker-net/datafaker/actions/runs/23618303854/job/68791305528?pr=1785: ``` java.lang.ClassCastException: class net.datafaker.providers.base.Color cannot be cast to class net.datafaker.providers.base.Vehicle (net.datafaker.providers.base.Color and net.datafaker.providers.base.Vehicle are in unnamed module of loader 'app') at net.datafaker.providers.base.BaseProviders.vehicle(BaseProviders.java:506) at net.datafaker.providers.base.VehicleLocaleTest.lambda$localeProviderListTest$3(VehicleLocaleTest.java:23) ``` Class `IdentityHashMap` is not thread-safe, all accesses to this class should be synchronized. --- src/main/java/net/datafaker/providers/base/BaseFaker.java | 4 +++- .../java/net/datafaker/providers/base/ObjectMethods.java | 5 +++-- src/main/java/net/datafaker/service/FakeValuesService.java | 3 ++- src/main/java/net/datafaker/service/FakerContext.java | 5 +++-- .../datafaker/transformations/JavaObjectTransformer.java | 6 ++++-- 5 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/datafaker/providers/base/BaseFaker.java b/src/main/java/net/datafaker/providers/base/BaseFaker.java index fe9553ca0..4dbd91a35 100644 --- a/src/main/java/net/datafaker/providers/base/BaseFaker.java +++ b/src/main/java/net/datafaker/providers/base/BaseFaker.java @@ -22,6 +22,8 @@ import java.util.function.Predicate; import java.util.function.Supplier; +import static java.util.Collections.synchronizedMap; + /** * Provides utility methods for generating fake strings, such as names, phone * numbers, addresses. generate random strings with given patterns @@ -32,7 +34,7 @@ public class BaseFaker implements BaseProviders { private static final Predicate> EVERY_PROVIDER_ALLOWED = t -> true; private final FakerContext context; private final FakeValuesService fakeValuesService; - private final Map, AbstractProvider> providersCache = new IdentityHashMap<>(); + private final Map, AbstractProvider> providersCache = synchronizedMap(new IdentityHashMap<>()); private final Predicate> whiteListPredicate; public BaseFaker() { diff --git a/src/main/java/net/datafaker/providers/base/ObjectMethods.java b/src/main/java/net/datafaker/providers/base/ObjectMethods.java index 2114b4324..950b7f9d5 100644 --- a/src/main/java/net/datafaker/providers/base/ObjectMethods.java +++ b/src/main/java/net/datafaker/providers/base/ObjectMethods.java @@ -7,11 +7,12 @@ import java.util.Set; import java.util.stream.Stream; +import static java.util.Collections.synchronizedMap; import static java.util.stream.Collectors.toMap; public class ObjectMethods { - private static final Map, Map> METHODS_BY_NAME = new IdentityHashMap<>(); - private static final Map, Map> METHODS_BY_RETURN_TYPE = new IdentityHashMap<>(); + private static final Map, Map> METHODS_BY_NAME = synchronizedMap(new IdentityHashMap<>()); + private static final Map, Map> METHODS_BY_RETURN_TYPE = synchronizedMap(new IdentityHashMap<>()); private static final Set IGNORED_METHODS = Set.of("equals", "hashCode", "toString", "Builder", "stream"); private static synchronized Map scanMethodsByName(Class clazz) { diff --git a/src/main/java/net/datafaker/service/FakeValuesService.java b/src/main/java/net/datafaker/service/FakeValuesService.java index 506daa85a..438ef1fe3 100644 --- a/src/main/java/net/datafaker/service/FakeValuesService.java +++ b/src/main/java/net/datafaker/service/FakeValuesService.java @@ -45,6 +45,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.emptyMap; +import static java.util.Collections.synchronizedMap; import static java.util.Locale.ROOT; import static java.util.Objects.requireNonNull; import static java.util.logging.Level.FINE; @@ -1147,7 +1148,7 @@ private Object[] coerceArguments(Method accessor, String[] args) { return coerced; } - private static final Map, Class> PRIMITIVE_WRAPPER_MAP = new IdentityHashMap<>(); + private static final Map, Class> PRIMITIVE_WRAPPER_MAP = synchronizedMap(new IdentityHashMap<>()); static { PRIMITIVE_WRAPPER_MAP.put(Boolean.TYPE, Boolean.class); diff --git a/src/main/java/net/datafaker/service/FakerContext.java b/src/main/java/net/datafaker/service/FakerContext.java index 2f6db1fa4..fdb5b960a 100644 --- a/src/main/java/net/datafaker/service/FakerContext.java +++ b/src/main/java/net/datafaker/service/FakerContext.java @@ -10,6 +10,7 @@ import java.util.Objects; import java.util.regex.Pattern; +import static java.util.Collections.synchronizedMap; import static java.util.Locale.ROOT; import static net.datafaker.service.FakeValuesService.DEFAULT_LOCALE; @@ -18,8 +19,8 @@ */ public class FakerContext { private static final Pattern LOCALE = Pattern.compile("[-_]"); - private static final Map> LOCALE_2_LOCALES_CHAIN = new IdentityHashMap<>(); - private static final Map STRING_LOCALE_HASH_MAP = new IdentityHashMap<>(); + private static final Map> LOCALE_2_LOCALES_CHAIN = synchronizedMap(new IdentityHashMap<>()); + private static final Map STRING_LOCALE_HASH_MAP = synchronizedMap(new IdentityHashMap<>()); public static final List DEFAULT_SINGLETON_LOCALE_LIST = List.of(DEFAULT_LOCALE); private static final Map LANGUAGE_DEFAULT_COUNTRY = Map.ofEntries( Map.entry("be", "BY"), diff --git a/src/main/java/net/datafaker/transformations/JavaObjectTransformer.java b/src/main/java/net/datafaker/transformations/JavaObjectTransformer.java index de9934216..683b10741 100644 --- a/src/main/java/net/datafaker/transformations/JavaObjectTransformer.java +++ b/src/main/java/net/datafaker/transformations/JavaObjectTransformer.java @@ -18,9 +18,11 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import static java.util.Collections.synchronizedMap; + public class JavaObjectTransformer implements Transformer { - private static final Map, Consumer> SCHEMA2CONSUMER = new IdentityHashMap<>(); - private static final Map, Constructor> CLASS2CONSTRUCTOR = new IdentityHashMap<>(); + private static final Map, Consumer> SCHEMA2CONSUMER = synchronizedMap(new IdentityHashMap<>()); + private static final Map, Constructor> CLASS2CONSTRUCTOR = synchronizedMap(new IdentityHashMap<>()); private Optional sourceClazz = Optional.empty();