diff --git a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java index a8b6a301f761..5858b6148e80 100644 --- a/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java +++ b/sdk/cosmos/azure-cosmos-tests/src/test/java/com/azure/cosmos/implementation/ImplementationBridgeHelpersTest.java @@ -11,7 +11,16 @@ import org.slf4j.LoggerFactory; import org.testng.annotations.Test; +import java.io.BufferedReader; +import java.io.InputStreamReader; import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CyclicBarrier; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; @@ -101,4 +110,340 @@ public void accessorInitialization() { fail("Failed with IllegalAccessException : ", e.getMessage()); } } + + /** + * Regression test for #48622 + * and #48585. + *

+ * Forks a fresh JVM that concurrently triggers {@code } of 12 different Cosmos classes + * from 12 threads synchronized via a {@link CyclicBarrier}. In a fresh JVM, {@code } + * runs for the first time — the only way to exercise the real deadlock scenario. A 30-second + * timeout detects the hang. Runs 5 invocations via TestNG ({@code invocationCount = 5}), + * each forking 3 child JVMs — totaling 15 fresh JVMs × 12 concurrent threads = 180 + * {@code } race attempts. + */ + @Test(groups = { "unit" }, invocationCount = 5) + public void concurrentAccessorInitializationShouldNotDeadlock() throws Exception { + + String javaHome = System.getProperty("java.home"); + String javaBin = javaHome + java.io.File.separator + "bin" + java.io.File.separator + "java"; + String classpath = System.getProperty("java.class.path"); + + List command = new ArrayList<>(); + command.add(javaBin); + + // --add-opens is only supported on JDK 9+ + try { + int majorVersion = Integer.parseInt(System.getProperty("java.specification.version").split("\\.")[0]); + if (majorVersion >= 9) { + command.add("--add-opens"); + command.add("java.base/java.lang=ALL-UNNAMED"); + } + } catch (NumberFormatException e) { + // JDK 8 returns "1.8" — first element is "1", which is < 9, so no --add-opens + } + + command.add("-cp"); + command.add(classpath); + command.add(ConcurrentClinitChildProcess.class.getName()); + + int timeoutSeconds = 30; + int runs = 3; + + for (int run = 1; run <= runs; run++) { + final int currentRun = run; + ProcessBuilder pb = new ProcessBuilder(command); + pb.redirectErrorStream(true); + Process process = pb.start(); + + // Drain stdout on a separate thread to prevent blocking if child JVM deadlocks. + // Without this, readLine() would block indefinitely and the timeout below + // would never be reached. + StringBuilder output = new StringBuilder(); + Thread gobbler = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append(System.lineSeparator()); + logger.info("[child-jvm-run-{}] {}", currentRun, line); + } + } catch (Exception e) { + // Process was destroyed — expected on timeout + } + }); + gobbler.setDaemon(true); + gobbler.start(); + + boolean completed = process.waitFor(timeoutSeconds, TimeUnit.SECONDS); + + if (!completed) { + process.destroyForcibly(); + gobbler.join(5000); + fail("Run " + run + ": Child JVM did not complete within " + timeoutSeconds + + " seconds — deadlock detected"); + } + + gobbler.join(5000); + int exitCode = process.exitValue(); + assertThat(exitCode) + .as("Run " + run + ": Child JVM exited with non-zero code. Output:\n" + output) + .isEqualTo(0); + } + } + + /** + * Entry point for the forked child JVM. Concurrently triggers {@code } of 12 different + * Cosmos classes that are involved in the circular initialization chain reported in the issues. + * Exits 0 on success, 1 on deadlock (timeout). + */ + public static final class ConcurrentClinitChildProcess { + public static void main(String[] args) { + int timeoutSeconds = 20; + + String[] classesToLoad = { + "com.azure.cosmos.CosmosAsyncClient", + "com.azure.cosmos.models.SqlParameter", + "com.azure.cosmos.models.FeedResponse", + "com.azure.cosmos.models.CosmosItemRequestOptions", + "com.azure.cosmos.CosmosAsyncContainer", + "com.azure.cosmos.util.CosmosPagedFluxDefaultImpl", + "com.azure.cosmos.CosmosClientBuilder", + "com.azure.cosmos.CosmosItemSerializer", + "com.azure.cosmos.CosmosDiagnostics", + "com.azure.cosmos.CosmosDiagnosticsContext", + "com.azure.cosmos.models.CosmosQueryRequestOptions", + "com.azure.cosmos.models.CosmosChangeFeedRequestOptions" + }; + + int threadCount = classesToLoad.length; + + // CyclicBarrier ensures all threads release at the exact same instant, + // maximizing the probability of concurrent collisions. Without it, + // thread startup stagger means earlier threads may finish before + // later threads start — hiding the deadlock. + CyclicBarrier barrier = new CyclicBarrier(threadCount); + ExecutorService executor = Executors.newFixedThreadPool(threadCount); + + try { + List> futures = new ArrayList<>(); + for (int i = 0; i < classesToLoad.length; i++) { + final String className = classesToLoad[i]; + final int idx = i; + futures.add(executor.submit(() -> { + try { + barrier.await(); + System.out.println("[Thread-" + idx + "] Loading " + className); + Class.forName(className); + System.out.println("[Thread-" + idx + "] Done."); + } catch (Exception e) { + throw new RuntimeException("Failed to load " + className, e); + } + })); + } + + boolean deadlock = false; + for (int i = 0; i < futures.size(); i++) { + try { + futures.get(i).get(timeoutSeconds, TimeUnit.SECONDS); + } catch (java.util.concurrent.TimeoutException e) { + System.err.println("DEADLOCK: Thread-" + i + " timed out after " + timeoutSeconds + "s"); + deadlock = true; + } catch (Exception e) { + Throwable root = e; + while (root.getCause() != null) { + root = root.getCause(); + } + System.err.println("Thread-" + i + " error: " + root); + } + } + + if (deadlock) { + System.exit(1); + } + + // Verify all classes are actually initialized + for (String className : classesToLoad) { + try { + // Class.forName with initialize=false just checks if already loaded + // If the class was loaded above, this returns immediately + Class cls = Class.forName(className, false, + ConcurrentClinitChildProcess.class.getClassLoader()); + // Verify the class is initialized by accessing its static state + // (calling a static method would trigger if not done, + // but we explicitly check it's already done) + System.out.println("Verified loaded: " + cls.getName()); + } catch (ClassNotFoundException e) { + System.err.println("Class not loaded: " + className); + System.exit(1); + } + } + + System.exit(0); + } finally { + executor.shutdownNow(); + } + } + } + + /** + * Enforces that every class targeted by {@code ensureClassInitialized()} in + * {@link ImplementationBridgeHelpers} registers its accessor during {@code } + * (i.e., has a {@code static { initialize(); }} block). + *

+ * Verification is behavioral, not source-based: a forked child JVM iterates every + * {@code *Helper} inner class, calls each {@code getXxxAccessor()} getter (which triggers + * {@code Class.forName()} → {@code }), and checks the accessor is non-null + * via reflection. If a class is missing {@code static { initialize(); }}, the accessor + * remains null and this test fails. + */ + @Test(groups = { "unit" }) + public void allAccessorClassesMustHaveStaticInitializerBlock() throws Exception { + String javaHome = System.getProperty("java.home"); + String javaBin = javaHome + java.io.File.separator + "bin" + java.io.File.separator + "java"; + String classpath = System.getProperty("java.class.path"); + + List command = new ArrayList<>(); + command.add(javaBin); + + try { + int majorVersion = Integer.parseInt(System.getProperty("java.specification.version").split("\\.")[0]); + if (majorVersion >= 9) { + command.add("--add-opens"); + command.add("java.base/java.lang=ALL-UNNAMED"); + } + } catch (NumberFormatException e) { + // JDK 8 + } + + command.add("-cp"); + command.add(classpath); + command.add(AccessorRegistrationChildProcess.class.getName()); + + ProcessBuilder pb = new ProcessBuilder(command); + pb.redirectErrorStream(true); + Process process = pb.start(); + + StringBuilder output = new StringBuilder(); + Thread gobbler = new Thread(() -> { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) { + String line; + while ((line = reader.readLine()) != null) { + output.append(line).append(System.lineSeparator()); + logger.info("[accessor-check] {}", line); + } + } catch (Exception e) { + // Process destroyed + } + }); + gobbler.setDaemon(true); + gobbler.start(); + + boolean completed = process.waitFor(60, TimeUnit.SECONDS); + if (!completed) { + process.destroyForcibly(); + gobbler.join(5000); + fail("Accessor registration check timed out after 60s. Output:\n" + output); + } + + gobbler.join(5000); + int exitCode = process.exitValue(); + assertThat(exitCode) + .as("Some accessor classes don't register their accessor during . Output:\n" + output) + .isEqualTo(0); + } + + /** + * Child process that verifies every {@code *Helper} inner class in + * {@link ImplementationBridgeHelpers} has its accessor registered after calling the + * corresponding {@code getXxxAccessor()} getter. Runs in a fresh JVM where no Cosmos + * classes have been loaded yet, so {@code } is triggered for the first time. + */ + public static final class AccessorRegistrationChildProcess { + public static void main(String[] args) throws Exception { + // Iterate all *Helper inner classes in ImplementationBridgeHelpers. + // For each, call the getXxxAccessor() getter which triggers ensureClassInitialized() + // → Class.forName() → . Then verify the accessor field is non-null. + + Class[] helpers = ImplementationBridgeHelpers.class.getDeclaredClasses(); + List failures = new ArrayList<>(); + + for (Class helper : helpers) { + if (!helper.getSimpleName().endsWith("Helper")) { + continue; + } + + // Find the accessor AtomicReference field + Field accessorField = null; + Field classLoadedField = null; + for (Field f : helper.getDeclaredFields()) { + if (f.getName().contains("accessor") && f.getType() == AtomicReference.class) { + accessorField = f; + } + if (f.getName().contains("ClassLoaded") && f.getType() == AtomicBoolean.class) { + classLoadedField = f; + } + } + + if (accessorField == null || classLoadedField == null) { + continue; + } + + // Check if the accessor is already set (from transitive of earlier classes) + accessorField.setAccessible(true); + AtomicReference ref = (AtomicReference) accessorField.get(null); + if (ref.get() != null) { + System.out.println("OK (already loaded): " + helper.getSimpleName()); + continue; + } + + // Find the target class name by looking for a getXxxAccessor method that calls + // ensureClassInitialized. We can't easily extract the string constant, so instead + // we call the getter and check if the accessor becomes non-null. + // The getter calls ensureClassInitialized() → Class.forName() → . + // If calls initialize(), the accessor is registered. + java.lang.reflect.Method getterMethod = null; + for (java.lang.reflect.Method m : helper.getDeclaredMethods()) { + if (m.getName().startsWith("get") && m.getName().endsWith("Accessor") + && m.getParameterCount() == 0 + && java.lang.reflect.Modifier.isStatic(m.getModifiers())) { + getterMethod = m; + break; + } + } + + if (getterMethod == null) { + continue; + } + + try { + Object result = getterMethod.invoke(null); + if (result == null) { + failures.add(helper.getSimpleName() + ": accessor is null after getter call — " + + "target class does not call initialize()"); + } else { + System.out.println("OK: " + helper.getSimpleName()); + } + } catch (Exception e) { + Throwable root = e; + while (root.getCause() != null) { + root = root.getCause(); + } + failures.add(helper.getSimpleName() + ": " + root.getClass().getSimpleName() + + " — " + root.getMessage()); + } + } + + if (failures.isEmpty()) { + System.out.println("All accessor classes register their accessor during ."); + System.exit(0); + } else { + System.err.println("FAILURES — the following classes do not register their accessor " + + "during (missing 'static { initialize(); }' block):"); + for (String f : failures) { + System.err.println(" " + f); + } + System.exit(1); + } + } + } } diff --git a/sdk/cosmos/azure-cosmos/CHANGELOG.md b/sdk/cosmos/azure-cosmos/CHANGELOG.md index 818e49226a40..4e5a1013487e 100644 --- a/sdk/cosmos/azure-cosmos/CHANGELOG.md +++ b/sdk/cosmos/azure-cosmos/CHANGELOG.md @@ -7,7 +7,8 @@ #### Breaking Changes #### Bugs Fixed -Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/) +* Fixing an NPE caused due to boxed Boolean conversion. - See [PR 48656](https://github.com/Azure/azure-sdk-for-java/pull/48656/) +* Fixed JVM `` deadlock when multiple threads concurrently trigger Cosmos SDK class loading for the first time. - See [PR 48667](https://github.com/Azure/azure-sdk-for-java/pull/48667) #### Other Changes diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosDiagnosticsContext.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosDiagnosticsContext.java index a5adfae71043..9846bda021ba 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosDiagnosticsContext.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosDiagnosticsContext.java @@ -1201,4 +1201,6 @@ public Integer getTargetMaxMicroBatchSize(CosmosDiagnosticsContext ctx) { } }); } + + static { initialize(); } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosItemSerializer.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosItemSerializer.java index f66dff9a21c1..e36ae3c5fd03 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosItemSerializer.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosItemSerializer.java @@ -30,6 +30,9 @@ */ public abstract class CosmosItemSerializer { + // Register the accessor before any static fields that may trigger other classes' + // which need this accessor (e.g., DefaultCosmosItemSerializer). + static { initialize(); } /** * Gets the default Cosmos item serializer. This serializer is used by default when no custom serializer is @@ -163,6 +166,4 @@ public ObjectMapper getItemObjectMapper(CosmosItemSerializer serializer) { } }); } - - static { initialize(); } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosRequestContext.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosRequestContext.java index f2a352b93f1b..be6538430345 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosRequestContext.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/CosmosRequestContext.java @@ -217,4 +217,6 @@ public CosmosRequestContext create(OverridableRequestOptions requestOptions) { } ); } + + static { initialize(); } } diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsBase.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsBase.java index ec9168a3a36f..920c11c1cbee 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsBase.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsBase.java @@ -30,7 +30,7 @@ */ public abstract class CosmosQueryRequestOptionsBase> implements OverridableRequestOptions { private final static ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.CosmosDiagnosticsThresholdsAccessor thresholdsAccessor = - ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosAsyncClientAccessor(); + ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosDiagnosticsThresholdsAccessor(); private ConsistencyLevel consistencyLevel; private ReadConsistencyStrategy readConsistencyStrategy; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsImpl.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsImpl.java index 9c51cf958137..cfdadd8cc9fe 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsImpl.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/CosmosQueryRequestOptionsImpl.java @@ -14,7 +14,7 @@ public final class CosmosQueryRequestOptionsImpl extends CosmosQueryRequestOptionsBase { private final static ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.CosmosDiagnosticsThresholdsAccessor thresholdsAccessor = - ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosAsyncClientAccessor(); + ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosDiagnosticsThresholdsAccessor(); private String partitionKeyRangeId; private Boolean scanInQueryEnabled; private Boolean emitVerboseTracesInQuery; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java index c9a61ed0f231..e7da49e39ec6 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/implementation/ImplementationBridgeHelpers.java @@ -111,6 +111,26 @@ public static void initializeAllAccessors() { BridgeInternal.initializeAllAccessors(); } + /** + * Forces the initialization of a single class (triggering its {@code } and thereby + * registering its accessor) without eagerly loading all other Cosmos SDK classes. + *

+ * This targeted approach replaces broad {@link #initializeAllAccessors()} calls inside + * individual {@code getXxxAccessor()} methods to prevent JVM-level {@code } deadlocks + * that occur when multiple threads concurrently trigger class loading of different Cosmos classes. + * + * @param className the fully-qualified name of the class whose accessor should be initialized + */ + private static void ensureClassInitialized(String className) { + try { + Class.forName(className, true, ImplementationBridgeHelpers.class.getClassLoader()); + } catch (ClassNotFoundException e) { + logger.error("Failed to load class for accessor initialization: {}", className, e); + throw new IllegalStateException( + "Unable to load class for accessor initialization: " + className, e); + } + } + public static final class CosmosClientBuilderHelper { private static final AtomicReference accessor = new AtomicReference<>(); private static final AtomicBoolean cosmosClientBuilderClassLoaded = new AtomicBoolean(false); @@ -129,7 +149,7 @@ public static void setCosmosClientBuilderAccessor(final CosmosClientBuilderAcces public static CosmosClientBuilderAccessor getCosmosClientBuilderAccessor() { if (!cosmosClientBuilderClassLoaded.get()) { logger.debug("Initializing CosmosClientBuilderAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosClientBuilder"); } CosmosClientBuilderAccessor snapshot = accessor.get(); @@ -190,7 +210,7 @@ public static void setPartitionKeyAccessor(final PartitionKeyAccessor newAccesso public static PartitionKeyAccessor getPartitionKeyAccessor() { if (!partitionKeyClassLoaded.get()) { logger.debug("Initializing PartitionKeyAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.PartitionKey"); } PartitionKeyAccessor snapshot = accessor.get(); @@ -226,7 +246,7 @@ public static void setDirectConnectionConfigAccessor(final DirectConnectionConfi public static DirectConnectionConfigAccessor getDirectConnectionConfigAccessor() { if (!directConnectionConfigClassLoaded.get()) { logger.debug("Initializing DirectConnectionConfigAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.DirectConnectionConfig"); } DirectConnectionConfigAccessor snapshot = accessor.get(); @@ -271,7 +291,7 @@ public static void setCosmosQueryRequestOptionsAccessor(final CosmosQueryRequest public static CosmosQueryRequestOptionsAccessor getCosmosQueryRequestOptionsAccessor() { if (!cosmosQueryRequestOptionsClassLoaded.get()) { logger.debug("Initializing CosmosQueryRequestOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosQueryRequestOptions"); } CosmosQueryRequestOptionsAccessor snapshot = accessor.get(); @@ -337,7 +357,7 @@ public static void setCosmosReadManyRequestOptionsAccessor(final CosmosReadManyR public static CosmosReadManyRequestOptionsAccessor getCosmosReadManyRequestOptionsAccessor() { if (!cosmosReadManyRequestOptionsClassLoaded.get()) { logger.debug("Initializing CosmosReadManyRequestOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosReadManyRequestOptions"); } CosmosReadManyRequestOptionsAccessor snapshot = accessor.get(); @@ -371,7 +391,7 @@ public static void setCosmosChangeFeedRequestOptionsAccessor(final CosmosChangeF public static CosmosChangeFeedRequestOptionsAccessor getCosmosChangeFeedRequestOptionsAccessor() { if (!cosmosChangeFeedRequestOptionsClassLoaded.get()) { logger.debug("Initializing CosmosChangeFeedRequestOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosChangeFeedRequestOptions"); } CosmosChangeFeedRequestOptionsAccessor snapshot = accessor.get(); @@ -425,7 +445,7 @@ public static void setCosmosItemRequestOptionsAccessor(final CosmosItemRequestOp public static CosmosItemRequestOptionsAccessor getCosmosItemRequestOptionsAccessor() { if (!cosmosItemRequestOptionsClassLoaded.get()) { logger.debug("Initializing CosmosItemRequestOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosItemRequestOptions"); } CosmosItemRequestOptionsAccessor snapshot = accessor.get(); @@ -469,7 +489,7 @@ public static void setCosmosBulkExecutionOptionsAccessor(final CosmosBulkExecuti public static CosmosBulkExecutionOptionsAccessor getCosmosBulkExecutionOptionsAccessor() { if (!cosmosBulkExecutionOptionsClassLoaded.get()) { logger.debug("Initializing CosmosBulkExecutionOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBulkExecutionOptions"); } CosmosBulkExecutionOptionsAccessor snapshot = accessor.get(); @@ -506,7 +526,7 @@ public static void setCosmosItemResponseBuilderAccessor(final CosmosItemResponse public static CosmosItemResponseBuilderAccessor getCosmosItemResponseBuilderAccessor() { if (!cosmosItemResponseClassLoaded.get()) { logger.debug("Initializing CosmosItemResponseBuilderAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosItemResponse"); } CosmosItemResponseBuilderAccessor snapshot = accessor.get(); @@ -562,7 +582,7 @@ public static void setCosmosClientAccessor(final CosmosClientAccessor newAccesso public static CosmosClientAccessor getCosmosClientAccessor() { if (!cosmosClientClassLoaded.get()) { logger.debug("Initializing CosmosClientAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosClient"); } CosmosClientAccessor snapshot = accessor.get(); @@ -597,7 +617,7 @@ public static void setCosmosContainerPropertiesAccessor(final CosmosContainerPro public static CosmosContainerPropertiesAccessor getCosmosContainerPropertiesAccessor() { if (!cosmosContainerPropertiesClassLoaded.get()) { logger.debug("Initializing CosmosContainerPropertiesAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosContainerProperties"); } CosmosContainerPropertiesAccessor snapshot = accessor.get(); @@ -634,7 +654,7 @@ public static void setCosmosPageFluxAccessor(final CosmosPageFluxAccessor ne public static CosmosPageFluxAccessor getCosmosPageFluxAccessor() { if (!cosmosPagedFluxClassLoaded.get()) { logger.debug("Initializing CosmosPageFluxAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.util.CosmosPagedFluxDefaultImpl"); } CosmosPageFluxAccessor snapshot = accessor.get(); @@ -669,7 +689,7 @@ public static void setCosmosAsyncDatabaseAccessor(final CosmosAsyncDatabaseA public static CosmosAsyncDatabaseAccessor getCosmosAsyncDatabaseAccessor() { if (!cosmosAsyncDatabaseClassLoaded.get()) { logger.debug("Initializing CosmosAsyncDatabaseAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosAsyncDatabase"); } CosmosAsyncDatabaseAccessor snapshot = accessor.get(); @@ -705,7 +725,7 @@ public static void setBulkExecutionThresholdsAccessor(final CosmosBulkExecutionT public static CosmosBulkExecutionThresholdsStateAccessor getBulkExecutionThresholdsAccessor() { if (!cosmosBulkExecutionThresholdsStateClassLoaded.get()) { logger.debug("Initializing CosmosBulkExecutionThresholdsStateAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBulkExecutionThresholdsState"); } CosmosBulkExecutionThresholdsStateAccessor snapshot = accessor.get(); @@ -743,7 +763,7 @@ public static void setCosmosOperationDetailsAccessor(final CosmosOperationDetail public static CosmosOperationDetailsAccessor getCosmosOperationDetailsAccessor() { if (!cosmosOperationDetailsClassLoaded.get()) { logger.debug("Initializing CosmosOperationDetailsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosOperationDetails"); } CosmosOperationDetailsAccessor snapshot = accessor.get(); @@ -779,7 +799,7 @@ public static void setCosmosRequestContextAccessor(final CosmosRequestContextAcc public static CosmosRequestContextAccessor getCosmosRequestContextAccessor() { if (!cosmosRequestContextClassLoaded.get()) { logger.debug("Initializing CosmosRequestContextAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosRequestContext"); } CosmosRequestContextAccessor snapshot = accessor.get(); @@ -815,7 +835,7 @@ public static void setCosmosDiagnosticsAccessor(final CosmosDiagnosticsAccessor public static CosmosDiagnosticsAccessor getCosmosDiagnosticsAccessor() { if (!cosmosDiagnosticsClassLoaded.get()) { logger.debug("Initializing CosmosDiagnosticsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosDiagnostics"); } CosmosDiagnosticsAccessor snapshot = accessor.get(); @@ -879,7 +899,7 @@ public static void setCosmosDiagnosticsContextAccessor(final CosmosDiagnosticsCo public static CosmosDiagnosticsContextAccessor getCosmosDiagnosticsContextAccessor() { if (!cosmosDiagnosticsContextClassLoaded.get()) { logger.debug("Initializing CosmosDiagnosticsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosDiagnosticsContext"); } CosmosDiagnosticsContextAccessor snapshot = accessor.get(); @@ -1013,7 +1033,7 @@ public static void setCosmosAsyncContainerAccessor(final CosmosAsyncContainerAcc public static CosmosAsyncContainerAccessor getCosmosAsyncContainerAccessor() { if (!cosmosAsyncContainerClassLoaded.get()) { logger.debug("Initializing CosmosAsyncContainerAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosAsyncContainer"); } CosmosAsyncContainerAccessor snapshot = accessor.get(); @@ -1096,7 +1116,7 @@ public static void setFeedResponseAccessor(final FeedResponseAccessor newAccesso public static FeedResponseAccessor getFeedResponseAccessor() { if (!feedResponseClassLoaded.get()) { logger.debug("Initializing FeedResponseAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.FeedResponse"); } FeedResponseAccessor snapshot = accessor.get(); @@ -1142,7 +1162,7 @@ private CosmosBatchRequestOptionsHelper() { public static CosmosBatchRequestOptionsAccessor getCosmosBatchRequestOptionsAccessor() { if (!cosmosBatchRequestOptionsClassLoaded.get()) { logger.debug("Initializing CosmosBatchRequestOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBatchRequestOptions"); } CosmosBatchRequestOptionsAccessor snapshot = accessor.get(); @@ -1196,7 +1216,7 @@ private CosmosBatchOperationResultHelper() { public static CosmosBatchOperationResultAccessor getCosmosBatchOperationResultAccessor() { if (!cosmosBatchOperationResultClassLoaded.get()) { logger.debug("Initializing CosmosBatchOperationResultAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBatchOperationResult"); } CosmosBatchOperationResultAccessor snapshot = accessor.get(); @@ -1234,7 +1254,7 @@ private CosmosPatchOperationsHelper() { public static CosmosPatchOperationsAccessor getCosmosPatchOperationsAccessor() { if (!cosmosPatchOperationsClassLoaded.get()) { logger.debug("Initializing CosmosPatchOperationsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosPatchOperations"); } CosmosPatchOperationsAccessor snapshot = accessor.get(); @@ -1269,7 +1289,7 @@ private CosmosBatchHelper() { public static CosmosBatchAccessor getCosmosBatchAccessor() { if (!cosmosBatchClassLoaded.get()) { logger.debug("Initializing CosmosBatchAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBatch"); } CosmosBatchAccessor snapshot = accessor.get(); @@ -1304,7 +1324,7 @@ private CosmosBulkItemResponseHelper() { public static CosmosBulkItemResponseAccessor getCosmosBulkItemResponseAccessor() { if (!cosmosBulkItemResponseClassLoaded.get()) { logger.debug("Initializing CosmosBulkItemResponseAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBulkItemResponse"); } CosmosBulkItemResponseAccessor snapshot = accessor.get(); @@ -1345,7 +1365,7 @@ private CosmosBatchResponseHelper() { public static CosmosBatchResponseAccessor getCosmosBatchResponseAccessor() { if (!cosmosBatchResponseClassLoaded.get()) { logger.debug("Initializing CosmosBatchResponseAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosBatchResponse"); } CosmosBatchResponseAccessor snapshot = accessor.get(); @@ -1396,7 +1416,7 @@ private CosmosAsyncClientEncryptionKeyHelper() { public static CosmosAsyncClientEncryptionKeyAccessor getCosmosAsyncClientEncryptionKeyAccessor() { if (!cosmosAsyncClientEncryptionKeyClassLoaded.get()) { logger.debug("Initializing CosmosAsyncClientEncryptionKeyAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosAsyncClientEncryptionKey"); } CosmosAsyncClientEncryptionKeyAccessor snapshot = accessor.get(); @@ -1440,7 +1460,7 @@ public static void setCosmosAsyncClientAccessor(final CosmosAsyncClientAccessor public static CosmosAsyncClientAccessor getCosmosAsyncClientAccessor() { if (!cosmosAsyncClientClassLoaded.get()) { logger.debug("Initializing CosmosAsyncClientAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosAsyncClient"); } CosmosAsyncClientAccessor snapshot = accessor.get(); @@ -1513,7 +1533,7 @@ public static void setHttp2ConnectionConfigAccessor(final Http2ConnectionConfigA public static Http2ConnectionConfigAccessor getHttp2ConnectionConfigAccessor() { if (!http2ConnectionConfigClassLoaded.get()) { logger.debug("Initializing Http2ConnectionConfigAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.Http2ConnectionConfig"); } Http2ConnectionConfigAccessor snapshot = accessor.get(); @@ -1547,10 +1567,10 @@ public static void setCosmosDiagnosticsThresholdsAccessor(final CosmosDiagnostic } } - public static CosmosDiagnosticsThresholdsAccessor getCosmosAsyncClientAccessor() { + public static CosmosDiagnosticsThresholdsAccessor getCosmosDiagnosticsThresholdsAccessor() { if (!cosmosDiagnosticsThresholdsClassLoaded.get()) { logger.debug("Initializing CosmosDiagnosticsThresholds..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosDiagnosticsThresholds"); } CosmosDiagnosticsThresholdsAccessor snapshot = accessor.get(); @@ -1580,7 +1600,7 @@ private CosmosExceptionHelper() { public static CosmosExceptionAccessor getCosmosExceptionAccessor() { if (!cosmosExceptionClassLoaded.get()) { logger.debug("Initializing CosmosExceptionAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosException"); } CosmosExceptionAccessor snapshot = accessor.get(); @@ -1627,7 +1647,7 @@ private CosmosClientTelemetryConfigHelper() { public static CosmosClientTelemetryConfigAccessor getCosmosClientTelemetryConfigAccessor() { if (!cosmosClientTelemetryClassLoaded.get()) { logger.debug("Initializing CosmosClientTelemetryConfigAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosClientTelemetryConfig"); } CosmosClientTelemetryConfigAccessor snapshot = accessor.get(); @@ -1700,7 +1720,7 @@ private PriorityLevelHelper() { public static PriorityLevelAccessor getPriorityLevelAccessor() { if (!priorityLevelClassLoaded.get()) { logger.debug("Initializing PriorityLevelAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.PriorityLevel"); } PriorityLevelAccessor snapshot = accessor.get(); @@ -1739,7 +1759,7 @@ public static CosmosContainerIdentityAccessor getCosmosContainerIdentityAccessor if (!cosmosContainerIdentityClassLoaded.get()) { logger.debug("Initializing CosmosContainerIdentityAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.models.CosmosContainerIdentity"); } CosmosContainerIdentityAccessor snapshot = accessor.get(); @@ -1781,7 +1801,7 @@ public static CosmosContainerProactiveInitConfigAccessor getCosmosContainerProac if (!cosmosContainerProactiveInitConfigClassLoaded.get()) { logger.debug("Initializing CosmosContainerProactiveInitConfigAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosContainerProactiveInitConfig"); } CosmosContainerProactiveInitConfigAccessor snapshot = accessor.get(); @@ -1820,7 +1840,7 @@ public static CosmosSessionRetryOptionsAccessor getCosmosSessionRetryOptionsAcce if (!cosmosSessionRetryOptionsClassLoaded.get()) { logger.debug("Initializing cosmosSessionRetryOptionsAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.SessionRetryOptions"); } CosmosSessionRetryOptionsAccessor snapshot = accessor.get(); @@ -1870,7 +1890,7 @@ public static void setCosmosItemSerializerAccessor(final CosmosItemSerializerAcc public static CosmosItemSerializerAccessor getCosmosItemSerializerAccessor() { if (!cosmosItemSerializerClassLoaded.get()) { logger.debug("Initializing CosmosItemSerializerAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.CosmosItemSerializer"); } CosmosItemSerializerAccessor snapshot = accessor.get(); @@ -1913,7 +1933,7 @@ public static void setReadConsistencyStrategyAccessor(final ReadConsistencyStrat public static ReadConsistencyStrategyAccessor getReadConsistencyStrategyAccessor() { if (!readConsistencyStrategyClassLoaded.get()) { logger.debug("Initializing ReadConsistencyStrategyAccessor..."); - initializeAllAccessors(); + ensureClassInitialized("com.azure.cosmos.ReadConsistencyStrategy"); } ReadConsistencyStrategyAccessor snapshot = accessor.get(); diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosItemRequestOptions.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosItemRequestOptions.java index 72eb108a6428..8f5c93aadb37 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosItemRequestOptions.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosItemRequestOptions.java @@ -28,7 +28,7 @@ */ public class CosmosItemRequestOptions { private final static ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.CosmosDiagnosticsThresholdsAccessor thresholdsAccessor = - ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosAsyncClientAccessor(); + ImplementationBridgeHelpers.CosmosDiagnosticsThresholdsHelper.getCosmosDiagnosticsThresholdsAccessor(); private ConsistencyLevel consistencyLevel; private ReadConsistencyStrategy readConsistencyStrategy; diff --git a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosOperationDetails.java b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosOperationDetails.java index 818625f32d2f..687870e0fa31 100644 --- a/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosOperationDetails.java +++ b/sdk/cosmos/azure-cosmos/src/main/java/com/azure/cosmos/models/CosmosOperationDetails.java @@ -56,4 +56,6 @@ static void initialize() { .setCosmosOperationDetailsAccessor( CosmosOperationDetails::new); } + + static { initialize(); } } diff --git a/sdk/spring/azure-spring-data-cosmos/README.md b/sdk/spring/azure-spring-data-cosmos/README.md index acf2ecaef58a..ca39fb42bf49 100644 --- a/sdk/spring/azure-spring-data-cosmos/README.md +++ b/sdk/spring/azure-spring-data-cosmos/README.md @@ -1169,3 +1169,4 @@ or contact [opencode@microsoft.com][coc_contact] with any additional questions o [azure_cosmos_db_java_sdk_samples]: https://github.com/Azure-Samples/azure-cosmos-java-sql-api-samples +