You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by mp...@apache.org on 2025/11/26 11:05:58 UTC

(ignite-3) branch IGNITE-26536 created (now 92382d45c8c)

This is an automated email from the ASF dual-hosted git repository.

mpochatkin pushed a change to branch IGNITE-26536
in repository https://gitbox.apache.org/repos/asf/ignite-3.git


      at 92382d45c8c IGNITE-26536 Extract code deployment class loader to separate module

This branch includes the following new commits:

     new 92382d45c8c IGNITE-26536 Extract code deployment class loader to separate module

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



(ignite-3) 01/01: IGNITE-26536 Extract code deployment class loader to separate module

Posted by mp...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

mpochatkin pushed a commit to branch IGNITE-26536
in repository https://gitbox.apache.org/repos/asf/ignite-3.git

commit 92382d45c8c0fd9b0f706222884662eb31c24645
Author: Pochatkin Mikhail <m....@gmail.com>
AuthorDate: Wed Nov 26 14:05:40 2025 +0300

    IGNITE-26536 Extract code deployment class loader to separate module
---
 modules/client/build.gradle                        |  1 +
 .../apache/ignite/client/fakes/FakeCompute.java    |  4 +--
 .../build.gradle}                                  | 37 ++++++++--------------
 .../deployunit/loader/UnitsClassLoader.java}       | 22 ++++++-------
 .../loader/UnitsClassLoaderFactory.java}           |  8 ++---
 .../deployunit/loader/UnitsClassLoaderImpl.java}   |  8 ++---
 .../deployunit/loader/UnitsClasspath.java}         |  4 +--
 .../internal/deployunit/loader/UnitsContext.java}  | 12 +++----
 .../deployunit/loader/UnitsContextManager.java}    | 28 ++++++++--------
 .../loader/JobClassLoaderFactoryTest.java          | 22 ++++++-------
 .../deployunit}/loader/JobClassLoaderTest.java     |  8 ++---
 .../deployunit}/loader/JobContextManagerTest.java  | 26 +++++++--------
 .../deployunit}/util/DummyIgniteDeployment.java    |  2 +-
 .../resources/units/unit1/5.0.0/subdir/test.txt    |  0
 .../src/test/resources/units/unit1/5.0.0/test.txt  |  0
 modules/compute/build.gradle                       |  1 +
 .../compute/ClassLoaderExceptionsMapper.java       |  6 ++--
 .../internal/compute/ComputeComponentImpl.java     | 14 ++++----
 .../ignite/internal/compute/ComputeUtils.java      |  6 ++--
 .../internal/compute/JobExecutionContextImpl.java  |  8 ++---
 .../internal/compute/executor/ComputeExecutor.java |  6 ++--
 .../compute/executor/ComputeExecutorImpl.java      | 10 +++---
 .../internal/compute/ComputeComponentImplTest.java | 12 +++----
 .../compute/executor/ComputeExecutorTest.java      |  4 +--
 modules/runner/build.gradle                        |  1 +
 .../org/apache/ignite/internal/app/IgniteImpl.java |  6 ++--
 settings.gradle                                    |  2 ++
 27 files changed, 127 insertions(+), 131 deletions(-)

diff --git a/modules/client/build.gradle b/modules/client/build.gradle
index 0a1067fac6f..f0c84698d23 100644
--- a/modules/client/build.gradle
+++ b/modules/client/build.gradle
@@ -57,6 +57,7 @@ dependencies {
     testImplementation project(':ignite-cluster-management')
     testImplementation project(':ignite-compute')
     testImplementation project(':ignite-code-deployment')
+    testImplementation project(':ignite-code-deployment-classloader')
     testImplementation project(':ignite-eventlog')
     testImplementation testFixtures(project(':ignite-api'))
     testImplementation testFixtures(project(':ignite-core'))
diff --git a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeCompute.java b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeCompute.java
index 5df6d81b043..fa5060dccb3 100644
--- a/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeCompute.java
+++ b/modules/client/src/test/java/org/apache/ignite/client/fakes/FakeCompute.java
@@ -68,7 +68,7 @@ import org.apache.ignite.internal.compute.SharedComputeUtils;
 import org.apache.ignite.internal.compute.TaskStateImpl;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadata;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadataBuilder;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.apache.ignite.internal.hlc.HybridTimestamp;
 import org.apache.ignite.internal.network.ClusterNodeImpl;
 import org.apache.ignite.internal.network.InternalClusterNode;
@@ -132,7 +132,7 @@ public class FakeCompute implements IgniteComputeInternal {
         }
 
         if (jobClassName.startsWith("org.apache.ignite")) {
-            JobClassLoader jobClassLoader = new JobClassLoader(List.of(), this.getClass().getClassLoader());
+            UnitsClassLoader jobClassLoader = new UnitsClassLoader(List.of(), this.getClass().getClassLoader());
             Class<ComputeJob<Object, Object>> jobClass = ComputeUtils.jobClass(jobClassLoader, jobClassName);
             ComputeJob<Object, Object> job = ComputeUtils.instantiateJob(jobClass);
             CompletableFuture<Object> jobFut = job.executeAsync(
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java b/modules/code-deployment-classloader/build.gradle
similarity index 55%
copy from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java
copy to modules/code-deployment-classloader/build.gradle
index a01684bdc0f..16ebb0d6ffc 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java
+++ b/modules/code-deployment-classloader/build.gradle
@@ -15,30 +15,21 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+apply from: "$rootDir/buildscripts/java-core.gradle"
+apply from: "$rootDir/buildscripts/java-integration-test.gradle"
+apply from: "$rootDir/buildscripts/publishing.gradle"
+apply from: "$rootDir/buildscripts/java-junit5.gradle"
+apply from: "$rootDir/buildscripts/java-test-fixtures.gradle"
 
-import java.util.function.Consumer;
+description = 'ignite-code-deployment-classloader'
 
-/**
- * Job context.
- */
-public class JobContext implements AutoCloseable {
-
-    private final JobClassLoader classLoader;
-
-    private final Consumer<JobContext> onClose;
-
-    public JobContext(JobClassLoader classLoader, Consumer<JobContext> onClose) {
-        this.classLoader = classLoader;
-        this.onClose = onClose;
-    }
+dependencies {
+    implementation libs.jetbrains.annotations
 
-    public JobClassLoader classLoader() {
-        return classLoader;
-    }
+    implementation project(':ignite-core')
+    implementation project(':ignite-api')
+    implementation project(':ignite-code-deployment')
 
-    @Override
-    public void close() {
-        onClose.accept(this);
-    }
-}
+    testImplementation project(':ignite-core')
+    testImplementation testFixtures(project(':ignite-core'))
+}
\ No newline at end of file
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoader.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoader.java
similarity index 82%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoader.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoader.java
index 8de3e65e462..46871c2943c 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoader.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoader.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import java.net.URL;
 import java.security.AccessController;
@@ -32,22 +32,22 @@ import org.apache.ignite.lang.IgniteException;
 /**
  * Implementation of {@link ClassLoader} that loads classes from specified component directories.
  */
-public class JobClassLoader implements AutoCloseable {
-    private static final IgniteLogger LOG = Loggers.forClass(JobClassLoader.class);
+public class UnitsClassLoader implements AutoCloseable {
+    private static final IgniteLogger LOG = Loggers.forClass(UnitsClassLoader.class);
 
     private final List<DisposableDeploymentUnit> units;
 
     private final ClassLoader parent;
 
-    private volatile JobClassLoaderImpl impl;
+    private volatile UnitsClassLoaderImpl impl;
 
     /**
-     * Creates new instance of {@link JobClassLoader}.
+     * Creates new instance of {@link UnitsClassLoader}.
      *
      * @param units Units to load classes from.
      * @param parent Parent class loader.
      */
-    public JobClassLoader(List<DisposableDeploymentUnit> units, ClassLoader parent) {
+    public UnitsClassLoader(List<DisposableDeploymentUnit> units, ClassLoader parent) {
         this.units = units;
         this.parent = parent;
     }
@@ -77,18 +77,18 @@ public class JobClassLoader implements AutoCloseable {
         return impl;
     }
 
-    private JobClassLoaderImpl createClassLoader() {
-        return AccessController.doPrivileged((PrivilegedAction<JobClassLoaderImpl>) () -> {
+    private UnitsClassLoaderImpl createClassLoader() {
+        return AccessController.doPrivileged((PrivilegedAction<UnitsClassLoaderImpl>) () -> {
             URL[] classpath = units.stream()
                     .map(DisposableDeploymentUnit::path)
-                    .flatMap(JobClasspath::collectClasspath)
+                    .flatMap(UnitsClasspath::collectClasspath)
                     .toArray(URL[]::new);
 
             if (LOG.isDebugEnabled()) {
                 LOG.debug("Created class loader with classpath: {}", Arrays.toString(classpath));
             }
 
-            return new JobClassLoaderImpl(units, classpath, parent);
+            return new UnitsClassLoaderImpl(units, classpath, parent);
         });
     }
 
@@ -105,7 +105,7 @@ public class JobClassLoader implements AutoCloseable {
         }
 
         try {
-            JobClassLoaderImpl impl0 = impl;
+            UnitsClassLoaderImpl impl0 = impl;
 
             if (impl0 != null) {
                 impl0.close();
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactory.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderFactory.java
similarity index 81%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactory.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderFactory.java
index bb277dffbff..ecf821dbf11 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactory.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderFactory.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import java.util.List;
 import org.apache.ignite.internal.deployunit.DisposableDeploymentUnit;
@@ -23,14 +23,14 @@ import org.apache.ignite.internal.deployunit.DisposableDeploymentUnit;
 /**
  * Creates a class loader for a job.
  */
-public class JobClassLoaderFactory {
+public class UnitsClassLoaderFactory {
     /**
      * Create a class loader for the specified units.
      *
      * @param units The units of the job.
      * @return The class loader.
      */
-    public JobClassLoader createClassLoader(List<DisposableDeploymentUnit> units) {
-        return new JobClassLoader(units, getClass().getClassLoader());
+    public UnitsClassLoader createClassLoader(List<DisposableDeploymentUnit> units) {
+        return new UnitsClassLoader(units, getClass().getClassLoader());
     }
 }
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderImpl.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderImpl.java
similarity index 93%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderImpl.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderImpl.java
index 0bd1db89b09..f3ee2b4f4fd 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClassLoaderImpl.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClassLoaderImpl.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -24,7 +24,7 @@ import java.util.List;
 import java.util.regex.Pattern;
 import org.apache.ignite.internal.deployunit.DisposableDeploymentUnit;
 
-class JobClassLoaderImpl extends URLClassLoader {
+class UnitsClassLoaderImpl extends URLClassLoader {
     /**
      * Pattern to match system packages.
      */
@@ -38,13 +38,13 @@ class JobClassLoaderImpl extends URLClassLoader {
     private final ClassLoader parent;
 
     /**
-     * Creates new instance of {@link JobClassLoader}.
+     * Creates new instance of {@link UnitsClassLoader}.
      *
      * @param urls URLs to load classes from.
      * @param units Units to load classes from.
      * @param parent Parent class loader.
      */
-    JobClassLoaderImpl(List<DisposableDeploymentUnit> units, URL[] urls, ClassLoader parent) {
+    public UnitsClassLoaderImpl(List<DisposableDeploymentUnit> units, URL[] urls, ClassLoader parent) {
         super("compute-job", urls, parent);
         this.units = units;
         this.parent = parent;
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClasspath.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClasspath.java
similarity index 97%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClasspath.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClasspath.java
index ec909fe33d0..31f283e5606 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobClasspath.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsClasspath.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import java.io.IOException;
 import java.net.MalformedURLException;
@@ -31,7 +31,7 @@ import java.util.stream.Stream;
 import org.apache.ignite.compute.ComputeException;
 import org.apache.ignite.lang.ErrorGroups.Compute;
 
-class JobClasspath {
+class UnitsClasspath {
     static Stream<URL> collectClasspath(Path unitDir) {
         if (Files.notExists(unitDir)) {
             throw new IllegalArgumentException("Unit does not exist: " + unitDir);
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContext.java
similarity index 75%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContext.java
index a01684bdc0f..e96045a9e79 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContext.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContext.java
@@ -15,25 +15,25 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import java.util.function.Consumer;
 
 /**
  * Job context.
  */
-public class JobContext implements AutoCloseable {
+public class UnitsContext implements AutoCloseable {
 
-    private final JobClassLoader classLoader;
+    private final UnitsClassLoader classLoader;
 
-    private final Consumer<JobContext> onClose;
+    private final Consumer<UnitsContext> onClose;
 
-    public JobContext(JobClassLoader classLoader, Consumer<JobContext> onClose) {
+    public UnitsContext(UnitsClassLoader classLoader, Consumer<UnitsContext> onClose) {
         this.classLoader = classLoader;
         this.onClose = onClose;
     }
 
-    public JobClassLoader classLoader() {
+    public UnitsClassLoader classLoader() {
         return classLoader;
     }
 
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContextManager.java b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContextManager.java
similarity index 89%
rename from modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContextManager.java
rename to modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContextManager.java
index 2cd5b32e1df..081a971f910 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/loader/JobContextManager.java
+++ b/modules/code-deployment-classloader/src/main/java/org/apache/ignite/internal/deployunit/loader/UnitsContextManager.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import static java.util.concurrent.CompletableFuture.completedFuture;
 import static java.util.concurrent.CompletableFuture.failedFuture;
@@ -43,10 +43,10 @@ import org.apache.ignite.lang.ErrorGroups.Compute;
 /**
  * Manages job context.
  */
-public class JobContextManager {
-    private static final IgniteLogger LOG = Loggers.forClass(JobContextManager.class);
+public class UnitsContextManager {
+    private static final IgniteLogger LOG = Loggers.forClass(UnitsContextManager.class);
 
-    private final RefCountedObjectPool<List<DeploymentUnit>, JobClassLoader> classLoaderPool = new RefCountedObjectPool<>();
+    private final RefCountedObjectPool<List<DeploymentUnit>, UnitsClassLoader> classLoaderPool = new RefCountedObjectPool<>();
 
     private final IgniteDeployment deployment;
     /**
@@ -57,7 +57,7 @@ public class JobContextManager {
     /**
      * The class loader factory.
      */
-    private final JobClassLoaderFactory classLoaderFactory;
+    private final UnitsClassLoaderFactory classLoaderFactory;
 
     /**
      * Constructor.
@@ -66,10 +66,10 @@ public class JobContextManager {
      * @param deploymentUnitAccessor The deployer service.
      * @param classLoaderFactory The class loader factory.
      */
-    public JobContextManager(
+    public UnitsContextManager(
             IgniteDeployment deployment,
             DeploymentUnitAccessor deploymentUnitAccessor,
-            JobClassLoaderFactory classLoaderFactory
+            UnitsClassLoaderFactory classLoaderFactory
     ) {
         this.deployment = deployment;
         this.deploymentUnitAccessor = deploymentUnitAccessor;
@@ -82,14 +82,14 @@ public class JobContextManager {
      * @param units The deployment units.
      * @return The class loader.
      */
-    public CompletableFuture<JobContext> acquireClassLoader(List<DeploymentUnit> units) {
-        CompletableFuture<JobContext> loaderFut = normalizeVersions(units)
+    public CompletableFuture<UnitsContext> acquireClassLoader(List<DeploymentUnit> units) {
+        CompletableFuture<UnitsContext> loaderFut = normalizeVersions(units)
                 .thenCompose(normalizedUnits -> checkUnitStatuses(normalizedUnits).thenApply(v -> normalizedUnits))
                 .thenCompose(normalizedUnits -> onDemandDeploy(normalizedUnits).thenApply(v -> normalizedUnits))
                 .thenApply(normalizedUnits -> classLoaderPool.acquire(normalizedUnits, this::createClassLoader))
-                .thenApply(loader -> new JobContext(loader, this::releaseClassLoader));
+                .thenApply(loader -> new UnitsContext(loader, this::releaseClassLoader));
 
-        CompletableFuture<JobContext> contextFut = loaderFut
+        CompletableFuture<UnitsContext> contextFut = loaderFut
                 .whenComplete((context, error) -> {
                     if (error != null) {
                         LOG.error("Failed to acquire class loader for units: " + units, error);
@@ -102,7 +102,7 @@ public class JobContextManager {
         // completes. We need to close the context in any case. The successful path should be handled in the caller. We can't handle it
         // there because we don't have the access to the context if the future failed.
         contextFut.exceptionally(e -> {
-            loaderFut.thenAccept(JobContext::close);
+            loaderFut.thenAccept(UnitsContext::close);
             return null;
         });
 
@@ -113,7 +113,7 @@ public class JobContextManager {
      * Creates a class loader for the given deployment units. The units will be acquired. The class loader will be closed when it is not
      * used by any other job.
      */
-    private JobClassLoader createClassLoader(List<DeploymentUnit> units) {
+    private UnitsClassLoader createClassLoader(List<DeploymentUnit> units) {
         List<DisposableDeploymentUnit> disposableDeploymentUnits = units.stream()
                 .map(deploymentUnitAccessor::acquire)
                 .collect(Collectors.toList());
@@ -124,7 +124,7 @@ public class JobContextManager {
      * Releases a class loader. If the class loader is not used by any other job, it will be closed and the deployment units will be
      * released.
      */
-    private void releaseClassLoader(JobContext jobContext) {
+    private void releaseClassLoader(UnitsContext jobContext) {
         List<DeploymentUnit> units = jobContext.classLoader().units().stream()
                 .map(DisposableDeploymentUnit::unit)
                 .collect(Collectors.toList());
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactoryTest.java b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderFactoryTest.java
similarity index 91%
rename from modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactoryTest.java
rename to modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderFactoryTest.java
index c44ff99b084..7c8e099db1d 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderFactoryTest.java
+++ b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderFactoryTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import static org.apache.ignite.internal.testframework.IgniteTestUtils.getPath;
 import static org.apache.ignite.internal.testframework.matchers.CompletableFutureMatcher.willBe;
@@ -55,9 +55,9 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
 
     private static final String JOB2_UTILITY_CLASS_NAME = "org.apache.ignite.internal.compute.Job2Utility";
 
-    private final Path unitsDir = getPath(JobClassLoaderFactory.class.getClassLoader().getResource("units"));
+    private final Path unitsDir = getPath(UnitsClassLoaderFactory.class.getClassLoader().getResource("units"));
 
-    private final JobClassLoaderFactory jobClassLoaderFactory = new JobClassLoaderFactory();
+    private final UnitsClassLoaderFactory jobClassLoaderFactory = new UnitsClassLoaderFactory();
 
     @Test
     @DisplayName("Load class with the same name from two different class loaders")
@@ -67,8 +67,8 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
         List<DisposableDeploymentUnit> units1 = toDisposableDeploymentUnits(new DeploymentUnit("unit1", "1.0.0"));
         List<DisposableDeploymentUnit> units2 = toDisposableDeploymentUnits(new DeploymentUnit("unit2", "2.0.0"));
 
-        try (JobClassLoader classLoader1 = jobClassLoaderFactory.createClassLoader(units1);
-                JobClassLoader classLoader2 = jobClassLoaderFactory.createClassLoader(units2)) {
+        try (UnitsClassLoader classLoader1 = jobClassLoaderFactory.createClassLoader(units1);
+                UnitsClassLoader classLoader2 = jobClassLoaderFactory.createClassLoader(units2)) {
             // then classes from the first unit are loaded from the first class loader
             Class<?> clazz1 = classLoader1.classLoader().loadClass(UNIT_JOB_CLASS_NAME);
             ComputeJob<Void, Integer> job1 = (ComputeJob<Void, Integer>) clazz1.getDeclaredConstructor().newInstance();
@@ -92,7 +92,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
                 new DeploymentUnit("unit1", "2.0.0")
         );
 
-        try (JobClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
+        try (UnitsClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
             Class<?> unitJobClass = classLoader.classLoader().loadClass(UNIT_JOB_CLASS_NAME);
             assertNotNull(unitJobClass);
 
@@ -120,7 +120,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
         List<DisposableDeploymentUnit> units = toDisposableDeploymentUnits(new DeploymentUnit("unit1", "3.0.1"));
 
         // then class from all jars are loaded
-        try (JobClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
+        try (UnitsClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
             Class<?> unitJobClass = classLoader.classLoader().loadClass(UNIT_JOB_CLASS_NAME);
             assertNotNull(unitJobClass);
 
@@ -140,7 +140,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
         List<DisposableDeploymentUnit> units = toDisposableDeploymentUnits(new DeploymentUnit("unit1", "3.0.2"));
 
         // then class from all jars are loaded
-        try (JobClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
+        try (UnitsClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
             Class<?> unitJobClass = classLoader.classLoader().loadClass(UNIT_JOB_CLASS_NAME);
             assertNotNull(unitJobClass);
 
@@ -160,7 +160,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
         List<DisposableDeploymentUnit> units = toDisposableDeploymentUnits(new DeploymentUnit("unit1", "4.0.0"));
 
         // then class loader throws an exception
-        try (JobClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
+        try (UnitsClassLoader classLoader = jobClassLoaderFactory.createClassLoader(units)) {
             assertThrows(ClassNotFoundException.class, () -> classLoader.classLoader().loadClass(UNIT_JOB_CLASS_NAME));
         }
     }
@@ -176,7 +176,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
         List<DisposableDeploymentUnit> units = toDisposableDeploymentUnits(new DeploymentUnit("unit1", "5.0.0"));
 
         // then the files are accessible
-        try (JobClassLoader jobClassLoader = jobClassLoaderFactory.createClassLoader(units)) {
+        try (UnitsClassLoader jobClassLoader = jobClassLoaderFactory.createClassLoader(units)) {
             ClassLoader classLoader = jobClassLoader.classLoader();
             String resource = Files.readString(getPath(classLoader.getResource("test.txt")));
             String subDirResource = Files.readString(getPath(classLoader.getResource("subdir/test.txt")));
@@ -250,7 +250,7 @@ class JobClassLoaderFactoryTest extends BaseIgniteAbstractTest {
             }
         };
 
-        try (JobClassLoader jobClassLoader = jobClassLoaderFactory.createClassLoader(slowList)) {
+        try (UnitsClassLoader jobClassLoader = jobClassLoaderFactory.createClassLoader(slowList)) {
             List<ClassLoader> classLoaders = new ArrayList<>();
 
             List<Thread> threads = IntStream.range(0, 10)
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderTest.java b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderTest.java
similarity index 93%
rename from modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderTest.java
rename to modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderTest.java
index f3baa75ffef..2f86aceef0f 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobClassLoaderTest.java
+++ b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobClassLoaderTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.arrayContaining;
@@ -90,7 +90,7 @@ class JobClassLoaderTest extends BaseIgniteAbstractTest {
         DisposableDeploymentUnit unit1 = mock(DisposableDeploymentUnit.class);
         DisposableDeploymentUnit unit2 = mock(DisposableDeploymentUnit.class);
 
-        try (JobClassLoader jobClassLoader = new JobClassLoader(List.of(unit1, unit2), parentClassLoader)) {
+        try (UnitsClassLoader jobClassLoader = new UnitsClassLoader(List.of(unit1, unit2), parentClassLoader)) {
             jobClassLoader.close();
             verify(unit1, times(1)).release();
             verify(unit2, times(1)).release();
@@ -104,12 +104,12 @@ class JobClassLoaderTest extends BaseIgniteAbstractTest {
         RuntimeException toBeThrown = new RuntimeException("Expected exception");
         doThrow(toBeThrown).when(unit1).release();
 
-        JobClassLoader jobClassLoader = new JobClassLoader(List.of(unit1, unit2), parentClassLoader);
+        UnitsClassLoader jobClassLoader = new UnitsClassLoader(List.of(unit1, unit2), parentClassLoader);
         IgniteException igniteException = assertThrows(IgniteException.class, jobClassLoader::close);
         assertThat(igniteException.getSuppressed(), arrayContaining(toBeThrown));
     }
 
-    private static class TestJobClassLoaderImpl extends JobClassLoaderImpl {
+    private static class TestJobClassLoaderImpl extends UnitsClassLoaderImpl {
         TestJobClassLoaderImpl(URL[] urls, List<DisposableDeploymentUnit> units, ClassLoader parent) {
             super(units, urls, parent);
         }
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobContextManagerTest.java b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobContextManagerTest.java
similarity index 89%
rename from modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobContextManagerTest.java
rename to modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobContextManagerTest.java
index 5d11721d2f9..4e599264d6f 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/loader/JobContextManagerTest.java
+++ b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/loader/JobContextManagerTest.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.loader;
+package org.apache.ignite.internal.deployunit.loader;
 
 import static java.util.concurrent.CompletableFuture.completedFuture;
 import static org.apache.ignite.deployment.version.Version.LATEST;
@@ -42,13 +42,13 @@ import java.util.List;
 import java.util.stream.Collectors;
 import org.apache.ignite.deployment.DeploymentUnit;
 import org.apache.ignite.deployment.version.Version;
-import org.apache.ignite.internal.compute.util.DummyIgniteDeployment;
 import org.apache.ignite.internal.deployunit.DeploymentUnitAccessorImpl;
 import org.apache.ignite.internal.deployunit.DisposableDeploymentUnit;
 import org.apache.ignite.internal.deployunit.FileDeployerService;
 import org.apache.ignite.internal.deployunit.IgniteDeployment;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitUnavailableException;
+import org.apache.ignite.internal.deployunit.util.DummyIgniteDeployment;
 import org.apache.ignite.internal.lang.IgniteInternalException;
 import org.apache.ignite.internal.testframework.BaseIgniteAbstractTest;
 import org.apache.ignite.internal.testframework.WorkDirectory;
@@ -65,7 +65,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 @ExtendWith(WorkDirectoryExtension.class)
 class JobContextManagerTest extends BaseIgniteAbstractTest {
 
-    private final Path unitsDir = getPath(JobClassLoaderFactory.class.getClassLoader().getResource("units"));
+    private final Path unitsDir = getPath(UnitsClassLoaderFactory.class.getClassLoader().getResource("units"));
 
     @WorkDirectory
     private Path workDir;
@@ -74,16 +74,16 @@ class JobContextManagerTest extends BaseIgniteAbstractTest {
     private IgniteDeployment deployment = new DummyIgniteDeployment(unitsDir);
 
     @Mock
-    private JobClassLoaderFactory jobClassLoaderFactory;
+    private UnitsClassLoaderFactory jobClassLoaderFactory;
 
-    private JobContextManager classLoaderManager;
+    private UnitsContextManager classLoaderManager;
 
     @BeforeEach
     void setUp() {
         FileDeployerService deployerService = new FileDeployerService("test");
         deployerService.initUnitsFolder(unitsDir, workDir.resolve("tempDeployment"));
 
-        classLoaderManager = new JobContextManager(
+        classLoaderManager = new UnitsContextManager(
                 deployment,
                 new DeploymentUnitAccessorImpl(deployerService),
                 jobClassLoaderFactory
@@ -105,11 +105,11 @@ class JobContextManagerTest extends BaseIgniteAbstractTest {
                 })
                 .collect(Collectors.toList());
 
-        JobClassLoader toBeReturned = new JobClassLoader(deploymentUnits, getClass().getClassLoader());
+        UnitsClassLoader toBeReturned = new UnitsClassLoader(deploymentUnits, getClass().getClassLoader());
         doReturn(toBeReturned)
                 .when(jobClassLoaderFactory).createClassLoader(deploymentUnits);
 
-        JobContext context = classLoaderManager.acquireClassLoader(units).join();
+        UnitsContext context = classLoaderManager.acquireClassLoader(units).join();
         assertSame(toBeReturned, context.classLoader());
 
         verify(jobClassLoaderFactory, times(1)).createClassLoader(deploymentUnits); // verify that class loader was created
@@ -131,10 +131,10 @@ class JobContextManagerTest extends BaseIgniteAbstractTest {
         Path version2path = unitDir.resolve(version2.version().toString());
         List<DisposableDeploymentUnit> disposableVersion2 = List.of(new DisposableDeploymentUnit(version2, version2path, () -> {}));
 
-        try (JobClassLoader toBeReturned1 = new JobClassLoader(
+        try (UnitsClassLoader toBeReturned1 = new UnitsClassLoader(
                 disposableVersion1,
                 getClass().getClassLoader());
-                JobClassLoader toBeReturned2 = new JobClassLoader(
+                UnitsClassLoader toBeReturned2 = new UnitsClassLoader(
                         disposableVersion2,
                         getClass().getClassLoader())
         ) {
@@ -148,16 +148,16 @@ class JobContextManagerTest extends BaseIgniteAbstractTest {
             Files.createDirectories(version1path).toFile().deleteOnExit();
 
             List<DeploymentUnit> units = List.of(new DeploymentUnit(unitName, LATEST));
-            JobContext context1 = classLoaderManager.acquireClassLoader(units).join();
+            UnitsContext context1 = classLoaderManager.acquireClassLoader(units).join();
 
             assertSame(toBeReturned1, context1.classLoader());
 
-            JobContext context2 = classLoaderManager.acquireClassLoader(units).join();
+            UnitsContext context2 = classLoaderManager.acquireClassLoader(units).join();
             assertSame(context1.classLoader(), context2.classLoader());
 
             Files.createDirectories(version2path).toFile().deleteOnExit();
 
-            JobContext context3 = classLoaderManager.acquireClassLoader(units).join();
+            UnitsContext context3 = classLoaderManager.acquireClassLoader(units).join();
             assertSame(toBeReturned2, context3.classLoader());
         }
     }
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/util/DummyIgniteDeployment.java b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/util/DummyIgniteDeployment.java
similarity index 98%
rename from modules/compute/src/test/java/org/apache/ignite/internal/compute/util/DummyIgniteDeployment.java
rename to modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/util/DummyIgniteDeployment.java
index be75511e21a..f23b65b6a32 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/util/DummyIgniteDeployment.java
+++ b/modules/code-deployment-classloader/src/test/java/org/apache/ignite/internal/deployunit/util/DummyIgniteDeployment.java
@@ -15,7 +15,7 @@
  * limitations under the License.
  */
 
-package org.apache.ignite.internal.compute.util;
+package org.apache.ignite.internal.deployunit.util;
 
 import static java.util.concurrent.CompletableFuture.completedFuture;
 import static java.util.concurrent.CompletableFuture.failedFuture;
diff --git a/modules/compute/src/test/resources/units/unit1/5.0.0/subdir/test.txt b/modules/code-deployment-classloader/src/test/resources/units/unit1/5.0.0/subdir/test.txt
similarity index 100%
rename from modules/compute/src/test/resources/units/unit1/5.0.0/subdir/test.txt
rename to modules/code-deployment-classloader/src/test/resources/units/unit1/5.0.0/subdir/test.txt
diff --git a/modules/compute/src/test/resources/units/unit1/5.0.0/test.txt b/modules/code-deployment-classloader/src/test/resources/units/unit1/5.0.0/test.txt
similarity index 100%
rename from modules/compute/src/test/resources/units/unit1/5.0.0/test.txt
rename to modules/code-deployment-classloader/src/test/resources/units/unit1/5.0.0/test.txt
diff --git a/modules/compute/build.gradle b/modules/compute/build.gradle
index 3180607d2d8..e6e43e20b91 100644
--- a/modules/compute/build.gradle
+++ b/modules/compute/build.gradle
@@ -29,6 +29,7 @@ dependencies {
     implementation project(':ignite-configuration-root')
     implementation project(':ignite-core')
     implementation project(':ignite-code-deployment')
+    implementation project(':ignite-code-deployment-classloader')
     implementation project(':ignite-cluster-management')
     implementation project(':ignite-placement-driver-api')
     implementation project(':ignite-binary-tuple')
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ClassLoaderExceptionsMapper.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ClassLoaderExceptionsMapper.java
index c2c6ff195d2..1f3f82d725d 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ClassLoaderExceptionsMapper.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ClassLoaderExceptionsMapper.java
@@ -21,9 +21,9 @@ import static org.apache.ignite.internal.util.ExceptionUtils.unwrapCompletionThr
 
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CompletionException;
-import org.apache.ignite.internal.compute.loader.JobContext;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitUnavailableException;
+import org.apache.ignite.internal.deployunit.loader.UnitsContext;
 
 class ClassLoaderExceptionsMapper {
     // <class_fqdn>. Deployment unit <deployment_unit_id_and ver> doesn't exist.
@@ -34,8 +34,8 @@ class ClassLoaderExceptionsMapper {
     private static final String DEPLOYMENT_UNIT_NOT_AVAILABLE_MSG = "%s. Deployment unit %s:%s can't be used: "
             + "[clusterStatus = %s, nodeStatus = %s]";
 
-    static CompletableFuture<JobContext> mapClassLoaderExceptions(
-            CompletableFuture<JobContext> future,
+    static CompletableFuture<UnitsContext> mapClassLoaderExceptions(
+            CompletableFuture<UnitsContext> future,
             String jobClassName
     ) {
         return future.handle((v, e) -> {
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeComponentImpl.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeComponentImpl.java
index fae7b4871d8..e393bbc48f9 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeComponentImpl.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeComponentImpl.java
@@ -39,13 +39,13 @@ import org.apache.ignite.internal.compute.configuration.ComputeConfiguration;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadataBuilder;
 import org.apache.ignite.internal.compute.executor.ComputeExecutor;
 import org.apache.ignite.internal.compute.executor.JobExecutionInternal;
-import org.apache.ignite.internal.compute.loader.JobContext;
-import org.apache.ignite.internal.compute.loader.JobContextManager;
 import org.apache.ignite.internal.compute.messaging.ComputeMessaging;
 import org.apache.ignite.internal.compute.messaging.RemoteJobExecution;
 import org.apache.ignite.internal.compute.task.DelegatingTaskExecution;
 import org.apache.ignite.internal.compute.task.JobSubmitter;
 import org.apache.ignite.internal.compute.task.TaskExecutionInternal;
+import org.apache.ignite.internal.deployunit.loader.UnitsContext;
+import org.apache.ignite.internal.deployunit.loader.UnitsContextManager;
 import org.apache.ignite.internal.eventlog.api.EventLog;
 import org.apache.ignite.internal.future.InFlightFutures;
 import org.apache.ignite.internal.lang.IgniteInternalException;
@@ -84,7 +84,7 @@ public class ComputeComponentImpl implements ComputeComponent, SystemViewProvide
 
     private final LogicalTopologyService logicalTopologyService;
 
-    private final JobContextManager jobContextManager;
+    private final UnitsContextManager jobContextManager;
 
     private final ComputeExecutor executor;
 
@@ -106,7 +106,7 @@ public class ComputeComponentImpl implements ComputeComponent, SystemViewProvide
             MessagingService messagingService,
             TopologyService topologyService,
             LogicalTopologyService logicalTopologyService,
-            JobContextManager jobContextManager,
+            UnitsContextManager jobContextManager,
             ComputeExecutor executor,
             ComputeConfiguration computeConfiguration,
             EventLog eventLog
@@ -133,7 +133,7 @@ public class ComputeComponentImpl implements ComputeComponent, SystemViewProvide
         }
 
         try {
-            CompletableFuture<JobContext> classLoaderFut = jobContextManager.acquireClassLoader(executionContext.units());
+            CompletableFuture<UnitsContext> classLoaderFut = jobContextManager.acquireClassLoader(executionContext.units());
 
             CompletableFuture<CancellableJobExecution<ComputeJobDataHolder>> future =
                     mapClassLoaderExceptions(classLoaderFut, executionContext.jobClassName())
@@ -337,7 +337,7 @@ public class ComputeComponentImpl implements ComputeComponent, SystemViewProvide
         messaging.start(executionContext -> executeLocally(executionContext, null));
     }
 
-    private JobExecutionInternal<ComputeJobDataHolder> execJob(JobContext context, ExecutionContext executionContext) {
+    private JobExecutionInternal<ComputeJobDataHolder> execJob(UnitsContext context, ExecutionContext executionContext) {
         try {
             return executor.executeJob(
                     executionContext.options(),
@@ -353,7 +353,7 @@ public class ComputeComponentImpl implements ComputeComponent, SystemViewProvide
     }
 
     private <I, M, T, R> TaskExecutionInternal<I, M, T, R> execTask(
-            JobContext context,
+            UnitsContext context,
             JobSubmitter<M, T> jobSubmitter,
             String taskClassName,
             ComputeEventMetadataBuilder metadataBuilder,
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeUtils.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeUtils.java
index 1431cb4706c..bc2c81ac68f 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeUtils.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/ComputeUtils.java
@@ -42,7 +42,6 @@ import org.apache.ignite.compute.task.MapReduceTask;
 import org.apache.ignite.compute.task.TaskExecutionContext;
 import org.apache.ignite.deployment.DeploymentUnit;
 import org.apache.ignite.deployment.version.Version;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
 import org.apache.ignite.internal.compute.message.DeploymentUnitMsg;
 import org.apache.ignite.internal.compute.message.ExecuteResponse;
 import org.apache.ignite.internal.compute.message.JobCancelResponse;
@@ -50,6 +49,7 @@ import org.apache.ignite.internal.compute.message.JobChangePriorityResponse;
 import org.apache.ignite.internal.compute.message.JobResultResponse;
 import org.apache.ignite.internal.compute.message.JobStateResponse;
 import org.apache.ignite.internal.compute.message.JobStatesResponse;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.apache.ignite.lang.IgniteCheckedException;
 import org.apache.ignite.lang.IgniteException;
 import org.apache.ignite.marshalling.Marshaller;
@@ -99,7 +99,7 @@ public class ComputeUtils {
      * @param <R> Compute job return type.
      * @return Compute job class.
      */
-    public static <T, R> Class<ComputeJob<T, R>> jobClass(JobClassLoader jobClassLoader, String jobClassName) {
+    public static <T, R> Class<ComputeJob<T, R>> jobClass(UnitsClassLoader jobClassLoader, String jobClassName) {
         try {
             return (Class<ComputeJob<T, R>>) Class.forName(jobClassName, true, jobClassLoader.classLoader());
         } catch (ClassNotFoundException e) {
@@ -147,7 +147,7 @@ public class ComputeUtils {
      * @param taskClassName Map reduce task class name.
      * @return Map reduce task class.
      */
-    public static <I, M, T, R> Class<MapReduceTask<I, M, T, R>> taskClass(JobClassLoader taskClassLoader, String taskClassName) {
+    public static <I, M, T, R> Class<MapReduceTask<I, M, T, R>> taskClass(UnitsClassLoader taskClassLoader, String taskClassName) {
         try {
             return (Class<MapReduceTask<I, M, T, R>>) Class.forName(taskClassName, true, taskClassLoader.classLoader());
         } catch (ClassNotFoundException e) {
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobExecutionContextImpl.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobExecutionContextImpl.java
index 44935641848..73c18bc0d7a 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobExecutionContextImpl.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobExecutionContextImpl.java
@@ -24,8 +24,8 @@ import java.util.concurrent.atomic.AtomicBoolean;
 import org.apache.ignite.Ignite;
 import org.apache.ignite.compute.JobExecutionContext;
 import org.apache.ignite.deployment.DeploymentUnitInfo;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
 import org.apache.ignite.internal.deployunit.DisposableDeploymentUnit;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.apache.ignite.internal.util.Lazy;
 import org.apache.ignite.table.partition.Partition;
 import org.jetbrains.annotations.Nullable;
@@ -38,7 +38,7 @@ public class JobExecutionContextImpl implements JobExecutionContext {
 
     private final AtomicBoolean isInterrupted;
 
-    private final JobClassLoader classLoader;
+    private final UnitsClassLoader classLoader;
 
     private final @Nullable Partition partition;
 
@@ -52,7 +52,7 @@ public class JobExecutionContextImpl implements JobExecutionContext {
      * @param classLoader Job class loader.
      * @param partition Partition associated with this job.
      */
-    public JobExecutionContextImpl(Ignite ignite, AtomicBoolean isInterrupted, JobClassLoader classLoader, @Nullable Partition partition) {
+    public JobExecutionContextImpl(Ignite ignite, AtomicBoolean isInterrupted, UnitsClassLoader classLoader, @Nullable Partition partition) {
         this.ignite = ignite;
         this.isInterrupted = isInterrupted;
         this.classLoader = classLoader;
@@ -86,7 +86,7 @@ public class JobExecutionContextImpl implements JobExecutionContext {
      *
      * @return Job class loader.
      */
-    public JobClassLoader classLoader() {
+    public UnitsClassLoader classLoader() {
         return classLoader;
     }
 
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutor.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutor.java
index c004cfa4a9d..a2583e776ff 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutor.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutor.java
@@ -20,9 +20,9 @@ package org.apache.ignite.internal.compute.executor;
 import org.apache.ignite.internal.compute.ComputeJobDataHolder;
 import org.apache.ignite.internal.compute.ExecutionOptions;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadataBuilder;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
 import org.apache.ignite.internal.compute.task.JobSubmitter;
 import org.apache.ignite.internal.compute.task.TaskExecutionInternal;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.jetbrains.annotations.Nullable;
 
 /**
@@ -32,7 +32,7 @@ public interface ComputeExecutor {
     JobExecutionInternal<ComputeJobDataHolder> executeJob(
             ExecutionOptions options,
             String jobClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             ComputeEventMetadataBuilder metadataBuilder,
             @Nullable ComputeJobDataHolder arg
     );
@@ -40,7 +40,7 @@ public interface ComputeExecutor {
     <I, M, T, R> TaskExecutionInternal<I, M, T, R> executeTask(
             JobSubmitter<M, T> jobSubmitter,
             String taskClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             ComputeEventMetadataBuilder metadataBuilder,
             @Nullable I arg
     );
diff --git a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutorImpl.java b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutorImpl.java
index 2ff5e119aec..da589a47294 100644
--- a/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutorImpl.java
+++ b/modules/compute/src/main/java/org/apache/ignite/internal/compute/executor/ComputeExecutorImpl.java
@@ -44,13 +44,13 @@ import org.apache.ignite.internal.compute.configuration.ComputeConfiguration;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadataBuilder;
 import org.apache.ignite.internal.compute.executor.platform.PlatformComputeTransport;
 import org.apache.ignite.internal.compute.executor.platform.dotnet.DotNetComputeExecutor;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
 import org.apache.ignite.internal.compute.queue.PriorityQueueExecutor;
 import org.apache.ignite.internal.compute.queue.QueueExecution;
 import org.apache.ignite.internal.compute.state.ComputeStateMachine;
 import org.apache.ignite.internal.compute.task.JobSubmitter;
 import org.apache.ignite.internal.compute.task.TaskExecutionContextImpl;
 import org.apache.ignite.internal.compute.task.TaskExecutionInternal;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.apache.ignite.internal.eventlog.api.EventLog;
 import org.apache.ignite.internal.hlc.ClockService;
 import org.apache.ignite.internal.logger.IgniteLogger;
@@ -115,7 +115,7 @@ public class ComputeExecutorImpl implements ComputeExecutor {
     public JobExecutionInternal<ComputeJobDataHolder> executeJob(
             ExecutionOptions options,
             String jobClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             ComputeEventMetadataBuilder metadataBuilder,
             @Nullable ComputeJobDataHolder arg
     ) {
@@ -167,7 +167,7 @@ public class ComputeExecutorImpl implements ComputeExecutor {
     private Callable<CompletableFuture<ComputeJobDataHolder>> getJobCallable(
             JobExecutorType executorType,
             String jobClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             @Nullable ComputeJobDataHolder arg,
             JobExecutionContext context
     ) {
@@ -193,7 +193,7 @@ public class ComputeExecutorImpl implements ComputeExecutor {
 
     private static Callable<CompletableFuture<ComputeJobDataHolder>> getJavaJobCallable(
             String jobClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             @Nullable ComputeJobDataHolder arg,
             JobExecutionContext context
     ) {
@@ -230,7 +230,7 @@ public class ComputeExecutorImpl implements ComputeExecutor {
     public <I, M, T, R> TaskExecutionInternal<I, M, T, R> executeTask(
             JobSubmitter<M, T> jobSubmitter,
             String taskClassName,
-            JobClassLoader classLoader,
+            UnitsClassLoader classLoader,
             ComputeEventMetadataBuilder metadataBuilder,
             @Nullable I arg
     ) {
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/ComputeComponentImplTest.java b/modules/compute/src/test/java/org/apache/ignite/internal/compute/ComputeComponentImplTest.java
index cca1eee1c61..5555657f1c5 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/ComputeComponentImplTest.java
+++ b/modules/compute/src/test/java/org/apache/ignite/internal/compute/ComputeComponentImplTest.java
@@ -77,9 +77,6 @@ import org.apache.ignite.internal.compute.configuration.ComputeConfiguration;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadata;
 import org.apache.ignite.internal.compute.executor.ComputeExecutor;
 import org.apache.ignite.internal.compute.executor.ComputeExecutorImpl;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
-import org.apache.ignite.internal.compute.loader.JobContext;
-import org.apache.ignite.internal.compute.loader.JobContextManager;
 import org.apache.ignite.internal.compute.message.ExecuteRequest;
 import org.apache.ignite.internal.compute.message.ExecuteResponse;
 import org.apache.ignite.internal.compute.message.JobCancelRequest;
@@ -96,6 +93,9 @@ import org.apache.ignite.internal.configuration.testframework.InjectConfiguratio
 import org.apache.ignite.internal.deployunit.DeploymentStatus;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
 import org.apache.ignite.internal.deployunit.exception.DeploymentUnitUnavailableException;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
+import org.apache.ignite.internal.deployunit.loader.UnitsContext;
+import org.apache.ignite.internal.deployunit.loader.UnitsContextManager;
 import org.apache.ignite.internal.eventlog.api.EventLog;
 import org.apache.ignite.internal.hlc.HybridClockImpl;
 import org.apache.ignite.internal.hlc.TestClockService;
@@ -145,7 +145,7 @@ class ComputeComponentImplTest extends BaseIgniteAbstractTest {
     private ComputeConfiguration computeConfiguration;
 
     @Mock
-    private JobContextManager jobContextManager;
+    private UnitsContextManager jobContextManager;
 
     private ComputeComponentImpl computeComponent;
 
@@ -163,8 +163,8 @@ class ComputeComponentImplTest extends BaseIgniteAbstractTest {
         lenient().when(ignite.name()).thenReturn(INSTANCE_NAME);
         lenient().when(topologyService.localMember().name()).thenReturn(INSTANCE_NAME);
 
-        JobClassLoader classLoader = new JobClassLoader(List.of(), getClass().getClassLoader());
-        JobContext jobContext = new JobContext(classLoader, ignored -> {});
+        UnitsClassLoader classLoader = new UnitsClassLoader(List.of(), getClass().getClassLoader());
+        UnitsContext jobContext = new UnitsContext(classLoader, ignored -> {});
         lenient().when(jobContextManager.acquireClassLoader(anyList()))
                 .thenReturn(completedFuture(jobContext));
 
diff --git a/modules/compute/src/test/java/org/apache/ignite/internal/compute/executor/ComputeExecutorTest.java b/modules/compute/src/test/java/org/apache/ignite/internal/compute/executor/ComputeExecutorTest.java
index 8e17a9ed0dd..a44f9819fa9 100644
--- a/modules/compute/src/test/java/org/apache/ignite/internal/compute/executor/ComputeExecutorTest.java
+++ b/modules/compute/src/test/java/org/apache/ignite/internal/compute/executor/ComputeExecutorTest.java
@@ -48,10 +48,10 @@ import org.apache.ignite.internal.compute.ExecutionOptions;
 import org.apache.ignite.internal.compute.SharedComputeUtils;
 import org.apache.ignite.internal.compute.configuration.ComputeConfiguration;
 import org.apache.ignite.internal.compute.events.ComputeEventMetadata;
-import org.apache.ignite.internal.compute.loader.JobClassLoader;
 import org.apache.ignite.internal.compute.state.InMemoryComputeStateMachine;
 import org.apache.ignite.internal.configuration.testframework.ConfigurationExtension;
 import org.apache.ignite.internal.configuration.testframework.InjectConfiguration;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoader;
 import org.apache.ignite.internal.eventlog.api.EventLog;
 import org.apache.ignite.internal.hlc.HybridClockImpl;
 import org.apache.ignite.internal.hlc.TestClockService;
@@ -78,7 +78,7 @@ class ComputeExecutorTest extends BaseIgniteAbstractTest {
 
     private ComputeExecutor computeExecutor;
 
-    private final JobClassLoader jobClassLoader = new JobClassLoader(List.of(), getClass().getClassLoader());
+    private final UnitsClassLoader jobClassLoader = new UnitsClassLoader(List.of(), getClass().getClassLoader());
 
     @BeforeEach
     void setUp() {
diff --git a/modules/runner/build.gradle b/modules/runner/build.gradle
index 558b3b6400b..cf642770d41 100644
--- a/modules/runner/build.gradle
+++ b/modules/runner/build.gradle
@@ -81,6 +81,7 @@ dependencies {
     implementation project(':ignite-distribution-zones')
     implementation project(':ignite-placement-driver')
     implementation project(':ignite-code-deployment')
+    implementation project(':ignite-code-deployment-classloader')
     implementation project(':ignite-security-api')
     implementation project(':ignite-security')
     implementation project(':ignite-catalog')
diff --git a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
index 02432b2aa6c..d85ed144ae0 100644
--- a/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
+++ b/modules/runner/src/main/java/org/apache/ignite/internal/app/IgniteImpl.java
@@ -100,8 +100,6 @@ import org.apache.ignite.internal.compute.IgniteComputeInternal;
 import org.apache.ignite.internal.compute.configuration.ComputeConfiguration;
 import org.apache.ignite.internal.compute.configuration.ComputeExtensionConfiguration;
 import org.apache.ignite.internal.compute.executor.ComputeExecutorImpl;
-import org.apache.ignite.internal.compute.loader.JobClassLoaderFactory;
-import org.apache.ignite.internal.compute.loader.JobContextManager;
 import org.apache.ignite.internal.compute.state.InMemoryComputeStateMachine;
 import org.apache.ignite.internal.configuration.ComponentWorkingDir;
 import org.apache.ignite.internal.configuration.ConfigurationDynamicDefaultsPatcherImpl;
@@ -126,6 +124,8 @@ import org.apache.ignite.internal.configuration.validation.ConfigurationValidato
 import org.apache.ignite.internal.deployunit.DeploymentManagerImpl;
 import org.apache.ignite.internal.deployunit.IgniteDeployment;
 import org.apache.ignite.internal.deployunit.configuration.DeploymentExtensionConfiguration;
+import org.apache.ignite.internal.deployunit.loader.UnitsClassLoaderFactory;
+import org.apache.ignite.internal.deployunit.loader.UnitsContextManager;
 import org.apache.ignite.internal.deployunit.metastore.DeploymentUnitStoreImpl;
 import org.apache.ignite.internal.disaster.system.ClusterIdService;
 import org.apache.ignite.internal.disaster.system.MetastorageRepairImpl;
@@ -1259,7 +1259,7 @@ public class IgniteImpl implements Ignite {
                 clusterSvc.messagingService(),
                 clusterSvc.topologyService(),
                 logicalTopologyService,
-                new JobContextManager(deploymentManagerImpl, deploymentManagerImpl.deploymentUnitAccessor(), new JobClassLoaderFactory()),
+                new UnitsContextManager(deploymentManagerImpl, deploymentManagerImpl.deploymentUnitAccessor(), new UnitsClassLoaderFactory()),
                 computeExecutor,
                 computeCfg,
                 eventLog
diff --git a/settings.gradle b/settings.gradle
index 1f17b095aa7..2a017bd8f88 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -77,6 +77,7 @@ include(':ignite-distribution-zones')
 include(':ignite-placement-driver')
 include(':ignite-placement-driver-api')
 include(':ignite-code-deployment')
+include(':ignite-code-deployment-classloader')
 include(':ignite-security-api')
 include(':ignite-security')
 include(':ignite-catalog')
@@ -160,6 +161,7 @@ project(":ignite-distribution-zones").projectDir = file('modules/distribution-zo
 project(":ignite-placement-driver").projectDir = file('modules/placement-driver')
 project(":ignite-placement-driver-api").projectDir = file('modules/placement-driver-api')
 project(":ignite-code-deployment").projectDir = file('modules/code-deployment')
+project(":ignite-code-deployment-classloader").projectDir = file('modules/code-deployment-classloader')
 project(":ignite-security").projectDir = file('modules/security')
 project(":ignite-security-api").projectDir = file('modules/security-api')
 project(":ignite-catalog").projectDir = file('modules/catalog')