This is an automated email from the ASF dual-hosted git repository.
apkhmv pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/ignite-3.git
The following commit(s) were added to refs/heads/main by this push:
new ed1534ce7c IGNITE-19475 Implement JobClassLoader (#2099)
ed1534ce7c is described below
commit ed1534ce7cf643ee576699cd181c2afcc933f5ce
Author: Ivan Gagarkin <[email protected]>
AuthorDate: Tue May 30 18:21:31 2023 +0400
IGNITE-19475 Implement JobClassLoader (#2099)
---
modules/api/build.gradle | 1 +
.../org/apache/ignite/compute/DeploymentUnit.java | 69 ++++++
.../ignite/compute}/version/UnitVersion.java | 11 +-
.../apache/ignite/compute}/version/Version.java | 4 +-
.../compute}/version/VersionParseException.java | 2 +-
.../ignite/compute/version/VersionTest.java} | 7 +-
.../deployunit/DeployMessagingService.java | 2 +-
.../ignite/internal/deployunit/DeployTracker.java | 2 +-
.../internal/deployunit/DeploymentManagerImpl.java | 2 +-
.../internal/deployunit/IgniteDeployment.java | 2 +-
.../ignite/internal/deployunit/UnitStatus.java | 2 +-
.../ignite/internal/deployunit/UnitStatuses.java | 2 +-
.../deployunit/metastore/DeploymentUnitStore.java | 2 +-
.../metastore/DeploymentUnitStoreImpl.java | 2 +-
.../internal/deployunit/metastore/key/UnitKey.java | 2 +-
.../metastore/key/UnitMetaSerializer.java | 2 +-
.../ignite/deployment/UnitMetaSerializerTest.java | 2 +-
.../metastore/DeploymentUnitStoreImplTest.java | 2 +-
.../ignite/internal/compute/JobClassLoader.java | 121 ++++++++++
.../internal/compute/JobClassLoaderFactory.java | 137 +++++++++++
modules/compute/src/test/README.md | 15 ++
.../compute/JobClassLoaderFactoryTest.java | 262 +++++++++++++++++++++
.../internal/compute/JobClassLoaderTest.java | 88 +++++++
.../units/test-units-1.0-SNAPSHOT-src.zip | Bin 0 -> 69386 bytes
.../units/unit1/1.0.0/unit1-1.0-SNAPSHOT.jar | Bin 0 -> 1693 bytes
.../units/unit1/2.0.0/unit2-1.0-SNAPSHOT.jar | Bin 0 -> 1681 bytes
.../units/unit1/3.0.1/unit1-1.0-SNAPSHOT.jar | Bin 0 -> 1693 bytes
.../units/unit1/3.0.1/unit2-1.0-SNAPSHOT.jar | Bin 0 -> 1681 bytes
.../unit1/3.0.2/subdir/unit2-1.0-SNAPSHOT.jar | Bin 0 -> 1681 bytes
.../units/unit1/3.0.2/unit1-1.0-SNAPSHOT.jar | Bin 0 -> 1693 bytes
.../units/unit1/4.0.0/unit1-1.0-corrupted.jar | Bin 0 -> 1693 bytes
.../resources/units/unit1/5.0.0/subdir/test.txt | 1 +
.../src/test/resources/units/unit1/5.0.0/test.txt | 1 +
.../units/unit2/1.0.0/unit1-1.0-SNAPSHOT.jar | Bin 0 -> 1693 bytes
.../units/unit2/2.0.0/unit2-1.0-SNAPSHOT.jar | Bin 0 -> 1681 bytes
.../java/org/apache/ignite/lang/ErrorGroups.java | 14 ++
.../deployment/DeploymentManagementController.java | 7 +-
.../handler/VersionParseExceptionHandler.java | 2 +-
.../internal/deployment/ItDeploymentUnitTest.java | 2 +-
39 files changed, 737 insertions(+), 31 deletions(-)
diff --git a/modules/api/build.gradle b/modules/api/build.gradle
index 9069b586a9..0155eb0ba7 100644
--- a/modules/api/build.gradle
+++ b/modules/api/build.gradle
@@ -29,6 +29,7 @@ dependencies {
testImplementation libs.hamcrest.optional
testImplementation libs.archunit.core
testImplementation libs.archunit.junit5
+ testImplementation testFixtures(project(":ignite-core"))
testFixturesAnnotationProcessor libs.micronaut.inject.annotation.processor
testFixturesImplementation project(":ignite-core")
diff --git
a/modules/api/src/main/java/org/apache/ignite/compute/DeploymentUnit.java
b/modules/api/src/main/java/org/apache/ignite/compute/DeploymentUnit.java
new file mode 100644
index 0000000000..2f152eb599
--- /dev/null
+++ b/modules/api/src/main/java/org/apache/ignite/compute/DeploymentUnit.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.compute;
+
+import org.apache.ignite.compute.version.Version;
+
+/**
+ * Deployment unit.
+ */
+public class DeploymentUnit {
+
+ /** Name of the deployment unit. */
+ private final String name;
+
+ /** Version of the deployment unit. */
+ private final Version version;
+
+ /**
+ * Constructor.
+ *
+ * @param name Name of the deployment unit.
+ * @param version Version of the deployment unit.
+ */
+ public DeploymentUnit(String name, Version version) {
+ this.name = name;
+ this.version = version;
+ }
+
+ /**
+ * Returns name of the deployment unit.
+ *
+ * @return Name of the deployment unit.
+ */
+ public String name() {
+ return name;
+ }
+
+ /**
+ * Returns version of the deployment unit.
+ *
+ * @return Version of the deployment unit.
+ */
+ public Version version() {
+ return version;
+ }
+
+ @Override
+ public String toString() {
+ return "DeploymentUnit{"
+ + "name='" + name + '\''
+ + ", version=" + version
+ + '}';
+ }
+}
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/UnitVersion.java
b/modules/api/src/main/java/org/apache/ignite/compute/version/UnitVersion.java
similarity index 94%
rename from
modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/UnitVersion.java
rename to
modules/api/src/main/java/org/apache/ignite/compute/version/UnitVersion.java
index c408595e64..c0a50177c7 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/UnitVersion.java
+++
b/modules/api/src/main/java/org/apache/ignite/compute/version/UnitVersion.java
@@ -15,17 +15,18 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.deployunit.version;
+package org.apache.ignite.compute.version;
import java.util.Objects;
/**
- * Implementation of {@link Version} interface based on the three numbers
format,
- * like x.x.x. where x is short number.
+ * Implementation of {@link Version} interface based on the three numbers
format, like x.x.x. where x is short number.
*/
-public class UnitVersion implements Version {
+class UnitVersion implements Version {
private final short major;
+
private final short minor;
+
private final short patch;
/**
@@ -35,7 +36,7 @@ public class UnitVersion implements Version {
* @param minor Minor part of version.
* @param patch Patch part of version.
*/
- public UnitVersion(short major, short minor, short patch) {
+ UnitVersion(short major, short minor, short patch) {
this.major = major;
this.minor = minor;
this.patch = patch;
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/Version.java
b/modules/api/src/main/java/org/apache/ignite/compute/version/Version.java
similarity index 95%
rename from
modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/Version.java
rename to
modules/api/src/main/java/org/apache/ignite/compute/version/Version.java
index f15393c3b4..a4d2a4366d 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/Version.java
+++ b/modules/api/src/main/java/org/apache/ignite/compute/version/Version.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.deployunit.version;
+package org.apache.ignite.compute.version;
/**
@@ -61,7 +61,7 @@ public interface Version extends Comparable<Version> {
* @return Version instance of
*/
static Version parseVersion(String s) {
- if ("latest".equals(s)) {
+ if ("latest".equalsIgnoreCase(s)) {
return LATEST;
}
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/VersionParseException.java
b/modules/api/src/main/java/org/apache/ignite/compute/version/VersionParseException.java
similarity index 96%
rename from
modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/VersionParseException.java
rename to
modules/api/src/main/java/org/apache/ignite/compute/version/VersionParseException.java
index 57f1ed9e10..ff5fa15300 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/version/VersionParseException.java
+++
b/modules/api/src/main/java/org/apache/ignite/compute/version/VersionParseException.java
@@ -15,7 +15,7 @@
* limitations under the License.
*/
-package org.apache.ignite.internal.deployunit.version;
+package org.apache.ignite.compute.version;
/**
* Throws when {@link Version} of deployment unit not parsable.
diff --git
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/version/VersionUnitTest.java
b/modules/api/src/test/java/org/apache/ignite/compute/version/VersionTest.java
similarity index 88%
rename from
modules/code-deployment/src/test/java/org/apache/ignite/deployment/version/VersionUnitTest.java
rename to
modules/api/src/test/java/org/apache/ignite/compute/version/VersionTest.java
index f2f436fef3..e209f7d582 100644
---
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/version/VersionUnitTest.java
+++
b/modules/api/src/test/java/org/apache/ignite/compute/version/VersionTest.java
@@ -15,21 +15,18 @@
* limitations under the License.
*/
-package org.apache.ignite.deployment.version;
+package org.apache.ignite.compute.version;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
-import org.apache.ignite.internal.deployunit.version.UnitVersion;
-import org.apache.ignite.internal.deployunit.version.Version;
-import org.apache.ignite.internal.deployunit.version.VersionParseException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* Unit tests for {@link Version}.
*/
-public class VersionUnitTest {
+public class VersionTest {
@Test
public void testParseCorrect() {
assertThat(Version.parseVersion("1.1.0"), is(new UnitVersion((short)
1, (short) 1, (short) 0)));
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployMessagingService.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployMessagingService.java
index 59c920684a..1b7cfde0f3 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployMessagingService.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployMessagingService.java
@@ -19,6 +19,7 @@ package org.apache.ignite.internal.deployunit;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.compute.version.Version;
import
org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
import org.apache.ignite.internal.deployunit.message.DeployUnitMessageTypes;
import org.apache.ignite.internal.deployunit.message.DeployUnitRequest;
@@ -31,7 +32,6 @@ import
org.apache.ignite.internal.deployunit.message.StopDeployResponseImpl;
import org.apache.ignite.internal.deployunit.message.UndeployUnitRequest;
import org.apache.ignite.internal.deployunit.message.UndeployUnitRequestImpl;
import org.apache.ignite.internal.deployunit.message.UndeployUnitResponseImpl;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.network.ChannelType;
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployTracker.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployTracker.java
index ad733c4618..644ab998a2 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployTracker.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeployTracker.java
@@ -22,7 +22,7 @@ import static
org.apache.ignite.internal.deployunit.metastore.key.UnitKey.cluste
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.ignite.internal.deployunit.version.Version;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.future.InFlightFutures;
import org.apache.ignite.lang.ByteArray;
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
index c083aea712..16f515b0b8 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/DeploymentManagerImpl.java
@@ -33,6 +33,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
+import org.apache.ignite.compute.version.Version;
import
org.apache.ignite.internal.cluster.management.ClusterManagementGroupManager;
import
org.apache.ignite.internal.deployunit.configuration.DeploymentConfiguration;
import
org.apache.ignite.internal.deployunit.exception.DeploymentUnitAlreadyExistsException;
@@ -40,7 +41,6 @@ import
org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundExc
import
org.apache.ignite.internal.deployunit.exception.DeploymentUnitReadException;
import org.apache.ignite.internal.deployunit.metastore.DeploymentUnitStore;
import org.apache.ignite.internal.deployunit.metastore.DeploymentUnitStoreImpl;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.logger.IgniteLogger;
import org.apache.ignite.internal.logger.Loggers;
import org.apache.ignite.internal.metastorage.MetaStorageManager;
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/IgniteDeployment.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/IgniteDeployment.java
index 525904a4a0..ee85618777 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/IgniteDeployment.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/IgniteDeployment.java
@@ -19,7 +19,7 @@ package org.apache.ignite.internal.deployunit;
import java.util.List;
import java.util.concurrent.CompletableFuture;
-import org.apache.ignite.internal.deployunit.version.Version;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.manager.IgniteComponent;
/**
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatus.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatus.java
index 136edd7c46..1e106f5b3b 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatus.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatus.java
@@ -17,7 +17,7 @@
package org.apache.ignite.internal.deployunit;
-import org.apache.ignite.internal.deployunit.version.Version;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
/**
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
index b8c9c1670d..346d8bb6be 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/UnitStatuses.java
@@ -22,7 +22,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
-import org.apache.ignite.internal.deployunit.version.Version;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
/**
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStore.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStore.java
index 39aeeec6d9..ae20ab30f8 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStore.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStore.java
@@ -21,9 +21,9 @@ import static
org.apache.ignite.internal.rest.api.deployment.DeploymentStatus.UP
import java.util.List;
import java.util.concurrent.CompletableFuture;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.UnitStatus;
import org.apache.ignite.internal.deployunit.UnitStatuses;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
/**
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStoreImpl.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStoreImpl.java
index a472cacd01..2d81bf22d9 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStoreImpl.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/DeploymentUnitStoreImpl.java
@@ -38,9 +38,9 @@ import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.UnitStatus;
import org.apache.ignite.internal.deployunit.UnitStatuses;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.metastorage.MetaStorageManager;
import org.apache.ignite.internal.metastorage.dsl.Condition;
import org.apache.ignite.internal.metastorage.dsl.Conditions;
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitKey.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitKey.java
index 355d5b57a3..c66a273b97 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitKey.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitKey.java
@@ -23,7 +23,7 @@ import java.util.Base64;
import java.util.Base64.Encoder;
import java.util.Objects;
import java.util.stream.Collectors;
-import org.apache.ignite.internal.deployunit.version.Version;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.lang.ByteArray;
/**
diff --git
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitMetaSerializer.java
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitMetaSerializer.java
index a5b514b138..aff0679bb3 100644
---
a/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitMetaSerializer.java
+++
b/modules/code-deployment/src/main/java/org/apache/ignite/internal/deployunit/metastore/key/UnitMetaSerializer.java
@@ -22,8 +22,8 @@ import static java.nio.charset.StandardCharsets.UTF_8;
import java.util.Base64;
import java.util.List;
import java.util.stream.Collectors;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.UnitStatus;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
/**
diff --git
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/UnitMetaSerializerTest.java
b/modules/code-deployment/src/test/java/org/apache/ignite/deployment/UnitMetaSerializerTest.java
index 23996ebdb7..625cccb2be 100644
---
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/UnitMetaSerializerTest.java
+++
b/modules/code-deployment/src/test/java/org/apache/ignite/deployment/UnitMetaSerializerTest.java
@@ -27,9 +27,9 @@ import static
org.junit.jupiter.params.provider.Arguments.arguments;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.UnitStatus;
import org.apache.ignite.internal.deployunit.metastore.key.UnitMetaSerializer;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
diff --git
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/metastore/DeploymentUnitStoreImplTest.java
b/modules/code-deployment/src/test/java/org/apache/ignite/deployment/metastore/DeploymentUnitStoreImplTest.java
index 2a0cba4003..1d0eb350e9 100644
---
a/modules/code-deployment/src/test/java/org/apache/ignite/deployment/metastore/DeploymentUnitStoreImplTest.java
+++
b/modules/code-deployment/src/test/java/org/apache/ignite/deployment/metastore/DeploymentUnitStoreImplTest.java
@@ -27,10 +27,10 @@ import static org.hamcrest.Matchers.equalTo;
import java.nio.file.Path;
import java.util.Collections;
import java.util.List;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.UnitStatus;
import org.apache.ignite.internal.deployunit.UnitStatuses;
import org.apache.ignite.internal.deployunit.metastore.DeploymentUnitStoreImpl;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.metastorage.MetaStorageManager;
import
org.apache.ignite.internal.metastorage.impl.StandaloneMetaStorageManager;
import org.apache.ignite.internal.metastorage.server.KeyValueStorage;
diff --git
a/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoader.java
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoader.java
new file mode 100644
index 0000000000..22483b3287
--- /dev/null
+++
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoader.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.compute;
+
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.regex.Pattern;
+import org.apache.ignite.lang.ErrorGroups.Compute;
+import org.apache.ignite.lang.IgniteException;
+
+/**
+ * Implementation of {@link ClassLoader} that loads classes from specified
component directories.
+ */
+public class JobClassLoader extends URLClassLoader {
+
+ /**
+ * Pattern to match system packages.
+ */
+ private static final Pattern SYSTEM_PACKAGES_PATTERN =
Pattern.compile("^(java|x|javax|org\\.apache\\.ignite)\\..*$");
+
+ /**
+ * Parent class loader.
+ */
+ private final ClassLoader parent;
+
+ /**
+ * Creates new instance of {@link JobClassLoader}.
+ *
+ * @param urls URLs to load classes from.
+ * @param parent Parent class loader.
+ */
+ JobClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ this.parent = parent;
+ }
+
+ /**
+ * Loads the class with the specified <a href="#binary-name">binary
name</a>. The implementation of this method searches for classes in
+ * the following order:
+ *
+ * <ol>
+ *
+ * <li><p> If the name starts with one of {@link
JobClassLoader#SYSTEM_PACKAGES_PATTERN},
+ * the loader delegates search to the parent. </p></li>
+ *
+ * <li><p> If the name doesn't start with one of {@link
JobClassLoader#SYSTEM_PACKAGES_PATTERN}:
+ *
+ * <ol>
+ * <li><p> Invoke {@link #findLoadedClass(String)} to check if the
class has already been loaded. </p></li>
+ * <li><p> Invoke the {@link #findClass(String)} method to find the
+ * class. </p></li>
+ * <li><p> Invoke the {@link #loadClass(String) loadClass} method
+ * on the parent class loader. </p></li>
+ * </ol></p></li>
+ *
+ * </ol>
+ *
+ * @param name The <a href="#binary-name">binary name</a> of the class.
+ * @return The resulting {@code Class} object.
+ * @throws ClassNotFoundException If the class was not found.
+ */
+ @Override
+ protected Class<?> loadClass(String name, boolean resolve) throws
ClassNotFoundException {
+ boolean isSystem = SYSTEM_PACKAGES_PATTERN.matcher(name).find();
+ if (isSystem) {
+ try {
+ return parent.loadClass(name);
+ } catch (ClassNotFoundException exception) {
+ return loadClassFromClasspath(name, resolve);
+ }
+ } else {
+ try {
+ return loadClassFromClasspath(name, resolve);
+ } catch (ClassNotFoundException exception) {
+ return parent.loadClass(name);
+ }
+ }
+ }
+
+ private Class<?> loadClassFromClasspath(String name, boolean resolve)
throws ClassNotFoundException {
+ Class<?> loadedClass = findLoadedClass(name);
+ if (loadedClass == null) {
+ Class<?> clazz = findClass(name);
+ if (resolve) {
+ resolveClass(clazz);
+ }
+ return clazz;
+ } else {
+ return loadedClass;
+ }
+ }
+
+ @Override
+ public void close() {
+ try {
+ super.close();
+ } catch (IOException e) {
+ throw new IgniteException(
+ Compute.CLASS_LOADER_ERR,
+ "Failed to close class loader: " + e.getMessage(),
+ e
+ );
+ }
+ }
+}
diff --git
a/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoaderFactory.java
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoaderFactory.java
new file mode 100644
index 0000000000..223cfb0b23
--- /dev/null
+++
b/modules/compute/src/main/java/org/apache/ignite/internal/compute/JobClassLoaderFactory.java
@@ -0,0 +1,137 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.compute;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Stream;
+import org.apache.ignite.compute.DeploymentUnit;
+import org.apache.ignite.compute.version.Version;
+import org.apache.ignite.internal.logger.IgniteLogger;
+import org.apache.ignite.internal.logger.Loggers;
+import org.apache.ignite.lang.ErrorGroups.Compute;
+import org.apache.ignite.lang.IgniteException;
+
+/**
+ * Creates a class loader for a job.
+ */
+public class JobClassLoaderFactory {
+ private static final IgniteLogger LOG =
Loggers.forClass(JobClassLoaderFactory.class);
+
+ /**
+ * Directory for units.
+ */
+ private final Path unitsDir;
+
+ /**
+ * The function to detect the last version of the unit.
+ */
+ private final Function<String, Version> detectLastUnitVersion;
+
+ /**
+ * Constructor.
+ *
+ * @param unitsDir The directory for units.
+ * @param detectLastUnitVersion The function to detect the last version of
the unit.
+ */
+ public JobClassLoaderFactory(Path unitsDir, Function<String, Version>
detectLastUnitVersion) {
+ this.unitsDir = unitsDir;
+ this.detectLastUnitVersion = detectLastUnitVersion;
+ }
+
+ /**
+ * Create a class loader for the specified units.
+ *
+ * @param units The units of the job.
+ * @return The class loader.
+ */
+ public JobClassLoader createClassLoader(List<DeploymentUnit> units) {
+ if (units.isEmpty()) {
+ throw new IllegalArgumentException("At least one unit must be
specified");
+ }
+
+ URL[] classPath = units.stream()
+ .map(this::constructPath)
+ .flatMap(JobClassLoaderFactory::collectClasspath)
+ .toArray(URL[]::new);
+
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("Classpath for job: " + Arrays.toString(classPath));
+ }
+
+ return new JobClassLoader(classPath, getClass().getClassLoader());
+ }
+
+ private Path constructPath(DeploymentUnit unit) {
+ Version version = unit.version() == Version.LATEST ?
detectLastUnitVersion.apply(unit.name()) : unit.version();
+ return unitsDir.resolve(unit.name()).resolve(version.toString());
+ }
+
+ private static Stream<URL> collectClasspath(Path unitDir) {
+ if (Files.notExists(unitDir)) {
+ throw new IllegalArgumentException("Unit does not exist: " +
unitDir);
+ }
+
+ if (!Files.isDirectory(unitDir)) {
+ throw new IllegalArgumentException("Unit is not a directory: " +
unitDir);
+ }
+
+ // Construct the "class path" for this unit
+ try {
+ ClasspathCollector classpathCollector = new
ClasspathCollector(unitDir);
+ Files.walkFileTree(unitDir, classpathCollector);
+ return classpathCollector.classpathAsStream();
+ } catch (IOException e) {
+ throw new IgniteException(
+ Compute.CLASS_PATH_ERR,
+ "Failed to construct classpath for job: " + unitDir,
+ e
+ );
+ }
+ }
+
+ private static class ClasspathCollector extends SimpleFileVisitor<Path> {
+ private final List<URL> classpath = new ArrayList<>();
+
+ private ClasspathCollector(Path base) throws MalformedURLException {
+ classpath.add(base.toAbsolutePath().toUri().toURL());
+ }
+
+ @Override
+ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
+ if (Files.isDirectory(file) || file.toString().endsWith(".jar")) {
+ classpath.add(file.toAbsolutePath().toUri().toURL());
+ }
+ return FileVisitResult.CONTINUE;
+ }
+
+ Stream<URL> classpathAsStream() {
+ return classpath.stream();
+ }
+ }
+}
diff --git a/modules/compute/src/test/README.md
b/modules/compute/src/test/README.md
new file mode 100644
index 0000000000..18966ac8ef
--- /dev/null
+++ b/modules/compute/src/test/README.md
@@ -0,0 +1,15 @@
+# ignite-compute
+
+## Unit tests
+`modules/compute/src/test/resources/units/test-units-1.0-SNAPSHOT-src.zip`
contains a zip archive with a
+test project which was used to create jars for
`org.apache.ignite.internal.compute.JobClassLoaderFactoryTest` tests.
+
+[unit1-1.0-SNAPSHOT.jar](resources%2Funits%2Funit2%2F1.0.0%2Funit1-1.0-SNAPSHOT.jar)
contains two classes
+which are used in tests:
+* `org.apache.ignite.internal.compute.unit1.Unit1` - extends Runnable and
returns 1 as Integer.
+* `org.my.job.compute.unit.Job1Utility`
+
+[unit2-1.0-SNAPSHOT.jar](resources%2Funits%2Funit2%2F2.0.0%2Funit2-1.0-SNAPSHOT.jar)
contains two classes
+which are used in tests:
+* `org.apache.ignite.internal.compute.unit1.Unit2` - extends Runnable and
returns "Hello World!" as String.
+* `org.my.job.compute.unit.Job2Utility`
diff --git
a/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderFactoryTest.java
b/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderFactoryTest.java
new file mode 100644
index 0000000000..19cd57f2ef
--- /dev/null
+++
b/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderFactoryTest.java
@@ -0,0 +1,262 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.compute;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.containsString;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.mockito.Mockito.doReturn;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.List;
+import java.util.concurrent.Callable;
+import java.util.function.Function;
+import org.apache.ignite.compute.DeploymentUnit;
+import org.apache.ignite.compute.version.Version;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.DisplayName;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class JobClassLoaderFactoryTest {
+ private final Path units =
Path.of(JobClassLoaderFactory.class.getClassLoader().getResource("units").getPath());
+
+ @Mock
+ private Function<String, Version> detectLastUnitVersion;
+
+ private JobClassLoaderFactory jobClassLoaderFactory;
+
+ @BeforeEach
+ void setUp() {
+ jobClassLoaderFactory = new JobClassLoaderFactory(units,
detectLastUnitVersion);
+ }
+
+ @Test
+ @DisplayName("Load class with the same name from two different class
loaders")
+ public void unit1() throws Exception {
+
+ // when unit1-1.0.0 is loaded and unit2-1.0.0 is loaded
+ List<DeploymentUnit> units1 = List.of(new DeploymentUnit("unit1",
Version.parseVersion("1.0.0")));
+ List<DeploymentUnit> units2 = List.of(new DeploymentUnit("unit2",
Version.parseVersion("2.0.0")));
+
+ try (JobClassLoader classLoader1 =
jobClassLoaderFactory.createClassLoader(units1);
+ JobClassLoader classLoader2 =
jobClassLoaderFactory.createClassLoader(units2)) {
+ // then classes from the first unit are loaded from the first
class loader
+ Class<?> clazz1 =
classLoader1.loadClass("org.my.job.compute.unit.UnitJob");
+ Callable<Object> job1 = (Callable<Object>)
clazz1.getDeclaredConstructor().newInstance();
+ Object result1 = job1.call();
+ assertSame(Integer.class, result1.getClass());
+ assertEquals(1, result1);
+
+ // and classes from the second unit are loaded from the second
class loader
+ Class<?> clazz2 =
classLoader2.loadClass("org.my.job.compute.unit.UnitJob");
+ Callable<Object> job2 = (Callable<Object>)
clazz2.getDeclaredConstructor().newInstance();
+ Object result2 = job2.call();
+ assertSame(String.class, result2.getClass());
+ assertEquals("Hello world!", result2);
+ }
+ }
+
+ @Test
+ @DisplayName("Load unit with the LATEST version")
+ public void unit2LatestVersion() throws Exception {
+
doReturn(Version.parseVersion("2.0.0")).when(detectLastUnitVersion).apply("unit2");
+
+ // when the version is LATEST
+ List<DeploymentUnit> units = List.of(new DeploymentUnit("unit2",
Version.LATEST));
+
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ // the the unit with the highest version is loaded
+ Class<?> clazz =
classLoader.loadClass("org.my.job.compute.unit.UnitJob");
+ Callable<Object> job = (Callable<Object>)
clazz.getDeclaredConstructor().newInstance();
+ Object result = job.call();
+ assertSame(String.class, result.getClass());
+ assertEquals("Hello world!", result);
+
+ // and the unit with the lower version is not loaded
+ assertThrows(ClassNotFoundException.class, () ->
classLoader.loadClass("org.my.job.compute.unit.Job1Utility"));
+ }
+ }
+
+ @Test
+ @DisplayName("Load both versions of the unit")
+ public void unit1BothVersions() throws Exception {
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("1.0.0")),
+ new DeploymentUnit("unit1", Version.parseVersion("2.0.0"))
+ );
+
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ Class<?> unitJobClass =
classLoader.loadClass("org.my.job.compute.unit.UnitJob");
+ assertNotNull(unitJobClass);
+
+ // and classes are loaded in the aplhabetical order
+ Callable<Object> job1 = (Callable<Object>)
unitJobClass.getDeclaredConstructor().newInstance();
+ Object result1 = job1.call();
+ assertSame(Integer.class, result1.getClass());
+ assertEquals(1, result1);
+
+ Class<?> job1UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job1Utility");
+ assertNotNull(job1UtilityClass);
+
+ Class<?> job2UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job2Utility");
+ assertNotNull(job2UtilityClass);
+
+ // classes from the different units are loaded from the same class
loader
+ assertSame(job1UtilityClass.getClassLoader(),
job2UtilityClass.getClassLoader());
+ }
+ }
+
+ @Test
+ @DisplayName("Unit with multiple jars")
+ public void unit1_3_0_1() throws Exception {
+
+ // when unit with multiple jars is loaded
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("3.0.1"))
+ );
+
+ // then class from all jars are loaded
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ Class<?> unitJobClass =
classLoader.loadClass("org.my.job.compute.unit.UnitJob");
+ assertNotNull(unitJobClass);
+
+ Class<?> job1UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job1Utility");
+ assertNotNull(job1UtilityClass);
+
+ Class<?> job2UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job2Utility");
+ assertNotNull(job2UtilityClass);
+ }
+ }
+
+ @Test
+ @DisplayName("Unit with multiple jars")
+ public void unit1_3_0_2() throws Exception {
+
+ // when unit with multiple jars is loaded
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("3.0.2"))
+ );
+
+ // then class from all jars are loaded
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ Class<?> unitJobClass =
classLoader.loadClass("org.my.job.compute.unit.UnitJob");
+ assertNotNull(unitJobClass);
+
+ Class<?> job1UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job1Utility");
+ assertNotNull(job1UtilityClass);
+
+ Class<?> job2UtilityClass =
classLoader.loadClass("org.my.job.compute.unit.Job2Utility");
+ assertNotNull(job2UtilityClass);
+ }
+ }
+
+ @Test
+ @DisplayName("Corrupted unit")
+ public void unit1_4_0_0() throws IOException {
+
+ // when unit with corrupted jar is loaded
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("4.0.0"))
+ );
+
+ // then class loader throws an exception
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ assertThrows(ClassNotFoundException.class, () ->
classLoader.loadClass("org.my.job.compute.unit.UnitJob"));
+ }
+ }
+
+ @Test
+ @DisplayName("Load resource from unit directory")
+ public void unit1_5_0_0() throws IOException {
+
+ String resourcePath = JobClassLoaderFactoryTest.class.getClassLoader()
+ .getResource("units/unit1/5.0.0/test.txt")
+ .getPath();
+ String expectedContent = Files.readString(Path.of(resourcePath));
+
+ // when unit with files is loaded
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("5.0.0"))
+ );
+
+ // then the files are accessible
+ try (JobClassLoader classLoader =
jobClassLoaderFactory.createClassLoader(units)) {
+ String resource =
Files.readString(Path.of(classLoader.getResource("test.txt").getPath()));
+ String subDirResource =
Files.readString(Path.of(classLoader.getResource("subdir/test.txt").getPath()));
+ assertEquals(expectedContent, resource);
+ assertEquals(expectedContent, subDirResource);
+
+ try (InputStream resourceAsStream =
classLoader.getResourceAsStream("test.txt");
+ InputStream subDirResourceAsStream =
classLoader.getResourceAsStream("subdir/test.txt")) {
+ String resourceStreamString = new
String(resourceAsStream.readAllBytes());
+ String subDirResourceStreamString = new
String(subDirResourceAsStream.readAllBytes());
+
+ assertEquals(expectedContent, resourceStreamString);
+ assertEquals(expectedContent, subDirResourceStreamString);
+ }
+ }
+ }
+
+ @Test
+ @DisplayName("Create class loader with empty units")
+ public void emptyUnits() {
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> jobClassLoaderFactory.createClassLoader(List.of())
+ );
+
+ assertThat(exception.getMessage(), containsString("At least one unit
must be specified"));
+ }
+
+ @Test
+ @DisplayName("Create class loader with non-existing unit")
+ public void nonExistingUnit() {
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("non-existing",
Version.parseVersion("1.0.0"))
+ );
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> jobClassLoaderFactory.createClassLoader(units)
+ );
+ assertThat(exception.getMessage(), containsString("Unit does not
exist:"));
+ }
+
+ @Test
+ @DisplayName("Create class loader with non-existing version")
+ public void nonExistingVersion() {
+ List<DeploymentUnit> units = List.of(
+ new DeploymentUnit("unit1", Version.parseVersion("-1.0.0"))
+ );
+ IllegalArgumentException exception = assertThrows(
+ IllegalArgumentException.class,
+ () -> jobClassLoaderFactory.createClassLoader(units)
+ );
+
+ assertThat(exception.getMessage(), containsString("Unit does not
exist:"));
+ }
+}
diff --git
a/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderTest.java
b/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderTest.java
new file mode 100644
index 0000000000..a1bed9da36
--- /dev/null
+++
b/modules/compute/src/test/java/org/apache/ignite/internal/compute/JobClassLoaderTest.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.ignite.internal.compute;
+
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.spy;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.net.URL;
+import java.util.stream.Stream;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+@ExtendWith(MockitoExtension.class)
+class JobClassLoaderTest {
+
+ @Mock
+ private ClassLoader parentClassLoader;
+
+ private static Stream<Arguments> testArguments() {
+ return Stream.of(
+ Arguments.of("java.lang.String", String.class),
+ Arguments.of("javax.lang.String", String.class),
+
Arguments.of("org.apache.ignite.internal.compute.JobExecutionContextImpl",
JobExecutionContextImpl.class)
+ );
+ }
+
+ @ParameterizedTest
+ @MethodSource("testArguments")
+ public void loadsSystemClassesFirst(String className, Class<?>
desiredClass) throws Exception {
+
+ doReturn(desiredClass).when(parentClassLoader).loadClass(className);
+
+ try (TestJobClassLoader jobClassLoader = spy(new
TestJobClassLoader(new URL[0], parentClassLoader))) {
+ assertSame(desiredClass, jobClassLoader.loadClass(className));
+ verify(jobClassLoader, never()).findClass(className);
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = {"org.apache.ignite.compute.unit1.UnitJob",
"java.lang.String", "javax.lang.String"})
+ public void loadsOwnClassIfSystemAbsent(String className) throws Exception
{
+
doThrow(ClassNotFoundException.class).when(parentClassLoader).loadClass(className);
+
+ try (TestJobClassLoader jobClassLoader = spy(new
TestJobClassLoader(new URL[0], parentClassLoader))) {
+ Class<TestJobClassLoader> toBeReturned = TestJobClassLoader.class;
+ doReturn(toBeReturned).when(jobClassLoader).findClass(className);
+
+ assertSame(toBeReturned, jobClassLoader.loadClass(className));
+ verify(parentClassLoader, times(1)).loadClass(className);
+ }
+ }
+
+ private static class TestJobClassLoader extends JobClassLoader {
+ TestJobClassLoader(URL[] urls, ClassLoader parent) {
+ super(urls, parent);
+ }
+
+ @Override
+ public Class<?> findClass(String name) throws ClassNotFoundException {
+ return super.findClass(name);
+ }
+ }
+}
diff --git
a/modules/compute/src/test/resources/units/test-units-1.0-SNAPSHOT-src.zip
b/modules/compute/src/test/resources/units/test-units-1.0-SNAPSHOT-src.zip
new file mode 100644
index 0000000000..53502ab4e5
Binary files /dev/null and
b/modules/compute/src/test/resources/units/test-units-1.0-SNAPSHOT-src.zip
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/1.0.0/unit1-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/1.0.0/unit1-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..88dd25aa3b
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/1.0.0/unit1-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/2.0.0/unit2-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/2.0.0/unit2-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..391c5b6964
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/2.0.0/unit2-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/3.0.1/unit1-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/3.0.1/unit1-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..88dd25aa3b
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/3.0.1/unit1-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/3.0.1/unit2-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/3.0.1/unit2-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..391c5b6964
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/3.0.1/unit2-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/3.0.2/subdir/unit2-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/3.0.2/subdir/unit2-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..391c5b6964
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/3.0.2/subdir/unit2-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/3.0.2/unit1-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit1/3.0.2/unit1-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..88dd25aa3b
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/3.0.2/unit1-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/4.0.0/unit1-1.0-corrupted.jar
b/modules/compute/src/test/resources/units/unit1/4.0.0/unit1-1.0-corrupted.jar
new file mode 100644
index 0000000000..5cb9c2c3e5
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit1/4.0.0/unit1-1.0-corrupted.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit1/5.0.0/subdir/test.txt
b/modules/compute/src/test/resources/units/unit1/5.0.0/subdir/test.txt
new file mode 100644
index 0000000000..f1fe480ba4
--- /dev/null
+++ b/modules/compute/src/test/resources/units/unit1/5.0.0/subdir/test.txt
@@ -0,0 +1 @@
+test resource
diff --git a/modules/compute/src/test/resources/units/unit1/5.0.0/test.txt
b/modules/compute/src/test/resources/units/unit1/5.0.0/test.txt
new file mode 100644
index 0000000000..f1fe480ba4
--- /dev/null
+++ b/modules/compute/src/test/resources/units/unit1/5.0.0/test.txt
@@ -0,0 +1 @@
+test resource
diff --git
a/modules/compute/src/test/resources/units/unit2/1.0.0/unit1-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit2/1.0.0/unit1-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..88dd25aa3b
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit2/1.0.0/unit1-1.0-SNAPSHOT.jar
differ
diff --git
a/modules/compute/src/test/resources/units/unit2/2.0.0/unit2-1.0-SNAPSHOT.jar
b/modules/compute/src/test/resources/units/unit2/2.0.0/unit2-1.0-SNAPSHOT.jar
new file mode 100644
index 0000000000..391c5b6964
Binary files /dev/null and
b/modules/compute/src/test/resources/units/unit2/2.0.0/unit2-1.0-SNAPSHOT.jar
differ
diff --git a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
index 32fb3285e9..f6ccf08c6d 100755
--- a/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
+++ b/modules/core/src/main/java/org/apache/ignite/lang/ErrorGroups.java
@@ -404,4 +404,18 @@ public class ErrorGroups {
/** General authentication error. */
public static final int COMMON_AUTHENTICATION_ERR =
AUTHENTICATION_ERR_GROUP.registerErrorCode(1);
}
+
+ /**
+ * Compute error group.
+ */
+ public static class Compute {
+ /** Compute error group. */
+ public static final ErrorGroup COMPUTE_ERR_GROUP =
ErrorGroup.newGroup("COMPUTE", 16);
+
+ /** Classpath error. */
+ public static final int CLASS_PATH_ERR =
COMPUTE_ERR_GROUP.registerErrorCode(1);
+
+ /** Class loader error. */
+ public static final int CLASS_LOADER_ERR =
COMPUTE_ERR_GROUP.registerErrorCode(2);
+ }
}
diff --git
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
index 4c291e589c..550aaada6a 100644
---
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
+++
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/DeploymentManagementController.java
@@ -25,11 +25,10 @@ import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.deployunit.DeploymentUnit;
import org.apache.ignite.internal.deployunit.IgniteDeployment;
import org.apache.ignite.internal.deployunit.UnitStatuses;
-import org.apache.ignite.internal.deployunit.version.UnitVersion;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.apache.ignite.internal.rest.api.deployment.DeploymentCodeApi;
import org.apache.ignite.internal.rest.api.deployment.DeploymentStatus;
import org.apache.ignite.internal.rest.api.deployment.UnitStatus;
@@ -50,12 +49,12 @@ public class DeploymentManagementController implements
DeploymentCodeApi {
public CompletableFuture<Boolean> deploy(String unitId, String
unitVersion, Publisher<CompletedFileUpload> unitContent) {
CompletableFuture<DeploymentUnit> result = new CompletableFuture<>();
unitContent.subscribe(new CompletedFileUploadSubscriber(result));
- return result.thenCompose(deploymentUnit ->
deployment.deployAsync(unitId, UnitVersion.parse(unitVersion), deploymentUnit));
+ return result.thenCompose(deploymentUnit ->
deployment.deployAsync(unitId, Version.parseVersion(unitVersion),
deploymentUnit));
}
@Override
public CompletableFuture<Boolean> undeploy(String unitId, String
unitVersion) {
- return deployment.undeployAsync(unitId,
UnitVersion.parse(unitVersion));
+ return deployment.undeployAsync(unitId,
Version.parseVersion(unitVersion));
}
@Override
diff --git
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/exception/handler/VersionParseExceptionHandler.java
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/exception/handler/VersionParseExceptionHandler.java
index 04afdacd4b..195ca6617c 100644
---
a/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/exception/handler/VersionParseExceptionHandler.java
+++
b/modules/rest/src/main/java/org/apache/ignite/internal/rest/deployment/exception/handler/VersionParseExceptionHandler.java
@@ -22,7 +22,7 @@ import io.micronaut.http.HttpRequest;
import io.micronaut.http.HttpResponse;
import io.micronaut.http.server.exceptions.ExceptionHandler;
import jakarta.inject.Singleton;
-import org.apache.ignite.internal.deployunit.version.VersionParseException;
+import org.apache.ignite.compute.version.VersionParseException;
import org.apache.ignite.internal.rest.api.Problem;
import org.apache.ignite.internal.rest.constants.HttpCode;
import org.apache.ignite.internal.rest.problem.HttpProblemResponse;
diff --git
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/deployment/ItDeploymentUnitTest.java
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/deployment/ItDeploymentUnitTest.java
index ce630ec4d4..26392e8b52 100644
---
a/modules/runner/src/integrationTest/java/org/apache/ignite/internal/deployment/ItDeploymentUnitTest.java
+++
b/modules/runner/src/integrationTest/java/org/apache/ignite/internal/deployment/ItDeploymentUnitTest.java
@@ -43,6 +43,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
+import org.apache.ignite.compute.version.Version;
import org.apache.ignite.internal.ClusterPerTestIntegrationTest;
import org.apache.ignite.internal.app.IgniteImpl;
import org.apache.ignite.internal.deployunit.DeploymentUnit;
@@ -51,7 +52,6 @@ import org.apache.ignite.internal.deployunit.UnitStatuses;
import org.apache.ignite.internal.deployunit.UnitStatuses.UnitStatusesBuilder;
import
org.apache.ignite.internal.deployunit.configuration.DeploymentConfiguration;
import
org.apache.ignite.internal.deployunit.exception.DeploymentUnitNotFoundException;
-import org.apache.ignite.internal.deployunit.version.Version;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;