Skip to content

Commit 58b8152

Browse files
authored
IGNITE-26706 Migration Tools: Allow coping with unnecessary third-party classes from SqlFunctionClasses (#6768)
1 parent 7bca939 commit 58b8152

File tree

16 files changed

+368
-22
lines changed

16 files changed

+368
-22
lines changed

migration-tools/modules/migration-tools-commons-tests/build.gradle

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ description = 'migration-tools-commons-tests'
2323

2424
configurations {
2525
unpackDependencies { transitive = false}
26+
fullSampleClusterDependencies
2627
}
2728

2829
dependencies {
@@ -37,6 +38,8 @@ dependencies {
3738
implementation files(layout.buildDirectory.dir("generated/unpackClassesFromDependencies"))
3839

3940
unpackDependencies libs.ignite2.core
41+
42+
fullSampleClusterDependencies project(':migration-tools-e2e-implementations-custom-classes')
4043
}
4144

4245
def unpackTask = tasks.register('unpackClassesFromDependencies', Copy) {
@@ -53,10 +56,30 @@ def unpackTask = tasks.register('unpackClassesFromDependencies', Copy) {
5356
"org/apache/ignite/cache/query/annotations/QuerySqlField\$Group.class"
5457
}
5558

59+
def createFullSampleClusterResources = tasks.register('createFullSampleClusterResources') {
60+
description = 'Generates the "fullsamplecluster" resource file.'
61+
62+
def outputFile = file("$buildDir/resources/main/fullsamplecluster")
63+
outputs.file(outputFile)
64+
65+
doLast {
66+
def depList = configurations.fullSampleClusterDependencies.resolvedConfiguration.resolvedArtifacts.collect { artifact ->
67+
artifact.file.path
68+
}
69+
70+
outputFile.parentFile.mkdirs()
71+
outputFile.text = depList.join('\n')
72+
}
73+
}
74+
5675
compileJava {
5776
dependsOn unpackTask
5877
}
5978

79+
processResources {
80+
dependsOn createFullSampleClusterResources
81+
}
82+
6083
/*sourceSets {
6184
main {
6285
java {

migration-tools/modules/migration-tools-commons-tests/src/main/java/org/apache/ignite/migrationtools/tests/clusters/FullSampleCluster.java

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,19 @@
1717

1818
package org.apache.ignite.migrationtools.tests.clusters;
1919

20+
import static java.nio.charset.StandardCharsets.UTF_8;
21+
22+
import java.io.BufferedReader;
23+
import java.io.IOException;
24+
import java.io.InputStream;
25+
import java.io.InputStreamReader;
2026
import java.nio.file.Path;
2127
import java.util.List;
28+
import java.util.stream.Collectors;
2229
import org.apache.ignite.migrationtools.tests.containers.Ignite2ClusterContainer;
2330
import org.apache.ignite.migrationtools.tests.containers.Ignite2ClusterWithSamples;
31+
import org.jetbrains.annotations.Nullable;
32+
import org.testcontainers.utility.MountableFile;
2433

2534
/** Cluster with all the samples from all the caches. */
2635
public class FullSampleCluster extends Ignite2ClusterWithSamples {
@@ -42,10 +51,32 @@ public FullSampleCluster() {
4251

4352
@Override
4453
protected Ignite2ClusterContainer createClusterContainers() {
45-
return new Ignite2ClusterContainer(
54+
var cluster = new Ignite2ClusterContainer(
4655
CLUSTER_CFG_PATH,
4756
TEST_CLUSTER_PATH,
4857
clusterNodeIds
4958
);
59+
60+
List<String> dependencies;
61+
@Nullable InputStream rs = FullSampleCluster.class.getResourceAsStream("/fullsamplecluster");
62+
if (rs == null) {
63+
throw new IllegalStateException("Could not find required resource for loading dependencies.");
64+
}
65+
66+
try (
67+
rs;
68+
InputStreamReader irs = new InputStreamReader(rs, UTF_8);
69+
BufferedReader r = new BufferedReader(irs)
70+
) {
71+
dependencies = r.lines().map(String::trim).collect(Collectors.toList());
72+
} catch (IOException e) {
73+
throw new RuntimeException(e);
74+
}
75+
76+
for (var path : dependencies) {
77+
cluster.withFileInClasspath(MountableFile.forHostPath(path));
78+
}
79+
80+
return cluster;
5081
}
5182
}

migration-tools/modules/migration-tools-commons-tests/src/main/java/org/apache/ignite/migrationtools/tests/containers/Ignite2ClusterContainer.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.testcontainers.containers.wait.strategy.Wait;
4141
import org.testcontainers.lifecycle.Startable;
4242
import org.testcontainers.lifecycle.Startables;
43+
import org.testcontainers.utility.DockerImageName;
4344
import org.testcontainers.utility.MountableFile;
4445

4546
/** Container of an Ignite 2 cluster. */
@@ -53,6 +54,8 @@ public class Ignite2ClusterContainer implements Startable {
5354

5455
private final boolean storagePathMappedToExternal;
5556

57+
private final String igniteHome;
58+
5659
/**
5760
* Port on host which binds container's 10800.
5861
*/
@@ -80,11 +83,18 @@ public Ignite2ClusterContainer(Network network, Path cfgFilePath, @Nullable Path
8083
this.containers = new ArrayList<>(nodeIds.size());
8184
this.storagePathMappedToExternal = storagePathOnHost != null;
8285

86+
String dockerImageName = System.getProperty("ignite2.docker.image");
87+
assert dockerImageName != null : "ignite2.docker.image must be defined";
88+
DockerImageName dockerImage = DockerImageName.parse(dockerImageName);
89+
90+
this.igniteHome = "/opt/ignite/apache-ignite";
91+
8392
for (int i = 0; i < nodeIds.size(); i++) {
8493
String hostname = "node" + (1 + i);
8594
String nodeId = nodeIds.get(i);
8695

8796
var nodeContainer = createIgnite2Container(
97+
dockerImage,
8898
hostname,
8999
nodeId,
90100
cfgFilePath,
@@ -103,17 +113,16 @@ public Network getNetwork() {
103113
}
104114

105115
private GenericContainer<?> createIgnite2Container(
116+
DockerImageName dockerImage,
106117
String hostName,
107118
String nodeId,
108119
Path cfgFilePath,
109120
@Nullable Path storagePathOnHost
110121
) {
111122
Consumer<OutputFrame> logConsumer = new CheckpointerLogConsumer();
112123
String heapSize = System.getProperty("ai2.sampleCluster.Xmx", "10g");
113-
String ignite2DockerImage = System.getProperty("ignite2.docker.image");
114-
assert ignite2DockerImage != null : "ignite2.docker.image must be defined";
115124

116-
GenericContainer<?> container = new GenericContainer<>(ignite2DockerImage);
125+
GenericContainer<?> container = new GenericContainer<>(dockerImage);
117126

118127
if (storagePathMappedToExternal) {
119128
container.withFileSystemBind(storagePathOnHost.toString(), "/storage", BindMode.READ_WRITE)
@@ -251,6 +260,17 @@ public String dockerHost() {
251260
return dockerHost;
252261
}
253262

263+
/**
264+
* Copies the supplied file to all the containers classpath.
265+
*
266+
* @param fileToCopy File to copy.
267+
*/
268+
public void withFileInClasspath(MountableFile fileToCopy) {
269+
for (var container : this.containers) {
270+
container.withCopyFileToContainer(fileToCopy, this.igniteHome + "/libs/");
271+
}
272+
}
273+
254274
private static class CheckpointerLogConsumer implements Consumer<OutputFrame> {
255275

256276
private List<Runnable> listeners = new CopyOnWriteArrayList<>();

migration-tools/modules/migration-tools-commons-tests/src/main/java/org/apache/ignite/migrationtools/tests/containers/Ignite2ClusterWithSamples.java

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,9 @@
2828
import org.junit.jupiter.api.Assertions;
2929
import org.junit.jupiter.api.extension.BeforeAllCallback;
3030
import org.junit.jupiter.api.extension.ExtensionContext;
31-
import org.testcontainers.containers.BindMode;
3231
import org.testcontainers.containers.GenericContainer;
3332
import org.testcontainers.containers.Network;
3433
import org.testcontainers.containers.output.OutputFrame;
35-
import org.testcontainers.containers.wait.strategy.Wait;
3634
import org.testcontainers.utility.MountableFile;
3735

3836
/** Ignite2ClusterWithSamples. */
@@ -59,22 +57,6 @@ public static boolean waitForCondition(BooleanSupplier cond, long sleepMillis, l
5957
return false;
6058
}
6159

62-
private static GenericContainer createIgnite2Container(String nodeId, String nodeName, Network network,
63-
Consumer<OutputFrame> logConsumer) {
64-
return new GenericContainer<>("apacheignite/ignite:2.15.0-jdk11")
65-
.withLabel("ai2.sample-cluster.node", nodeName)
66-
.withNetwork(network)
67-
.withNetworkAliases(nodeName)
68-
.withCopyFileToContainer(MountableFile.forHostPath(FullSampleCluster.CLUSTER_CFG_PATH), "/config-file.xml")
69-
.withFileSystemBind(FullSampleCluster.TEST_CLUSTER_PATH.toString(), "/storage", BindMode.READ_WRITE)
70-
.withEnv("CONFIG_URI", "/config-file.xml")
71-
.withEnv("IGNITE_WORK_DIR", "/storage")
72-
.withEnv("IGNITE_QUIET", "false")
73-
.withEnv("IGNITE_NODE_NAME", nodeId)
74-
.withLogConsumer(logConsumer)
75-
.waitingFor(Wait.forLogMessage(".*Node started .*", 1));
76-
}
77-
7860
protected abstract Ignite2ClusterContainer createClusterContainers();
7961

8062
private void recreateClusterFolder() throws InterruptedException, IOException {

migration-tools/modules/migration-tools-persistence/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ dependencies {
3232
implementation libs.commons.collections4
3333
implementation libs.jackson.databind
3434
implementation libs.slf4j.api
35+
implementation libs.bytebuddy
3536
compileOnly libs.spotbugs.annotations
3637

3738
testImplementation project(":migration-tools-commons-tests")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.migrationtools.persistence;
19+
20+
import static org.assertj.core.api.Assertions.assertThat;
21+
22+
import java.util.Collection;
23+
import java.util.List;
24+
import java.util.stream.Collectors;
25+
import org.apache.ignite.IgniteCheckedException;
26+
import org.apache.ignite.internal.processors.cache.CacheType;
27+
import org.apache.ignite.internal.processors.cache.DynamicCacheDescriptor;
28+
import org.apache.ignite.migrationtools.tests.clusters.FullSampleCluster;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.Test;
31+
import org.junit.jupiter.api.extension.ExtendWith;
32+
import org.junit.jupiter.params.ParameterizedTest;
33+
import org.junit.jupiter.params.provider.FieldSource;
34+
35+
@ExtendWith(FullSampleCluster.class)
36+
class MarshallerTest {
37+
private static final List<String> CACHE_NAMES = List.of(
38+
"MySimpleMap",
39+
"MyPersonPojoCache",
40+
"MyOrganizations",
41+
"MyIntArrCache",
42+
"MyListArrCache",
43+
"MyBinaryPersonPojoCache",
44+
"MyBinaryOrganizationCache",
45+
"MyBinaryTestCache"
46+
);
47+
48+
@ExtendWith(BasePersistentTestContext.class)
49+
private List<MigrationKernalContext> nodeContexts;
50+
51+
private MigrationKernalContext nodeCtx;
52+
53+
@BeforeEach
54+
void beforeEach() throws IgniteCheckedException {
55+
nodeCtx = nodeContexts.get(0);
56+
nodeCtx.start();
57+
}
58+
59+
@Test
60+
void loadAllTest() throws IgniteCheckedException {
61+
((MigrationCacheProcessor) nodeCtx.cache()).loadAllDescriptors();
62+
63+
Collection<DynamicCacheDescriptor> descriptors = nodeCtx.cache().persistentCaches().stream()
64+
.filter(c -> c.cacheType() == CacheType.USER)
65+
.collect(Collectors.toList());
66+
67+
assertThat(descriptors).hasSize(8);
68+
69+
assertThat(descriptors)
70+
.map(DynamicCacheDescriptor::cacheConfiguration)
71+
.doesNotContainNull();
72+
}
73+
74+
@ParameterizedTest
75+
@FieldSource("CACHE_NAMES")
76+
void loadEachTest(String cacheName) {
77+
var cacheDescriptor = nodeCtx.cache().cacheDescriptor(cacheName);
78+
assertThat(cacheDescriptor).isNotNull();
79+
assertThat(cacheDescriptor.cacheConfiguration()).isNotNull();
80+
}
81+
}

migration-tools/modules/migration-tools-persistence/src/main/java/org/apache/ignite/migrationtools/persistence/MigrationKernalContext.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import org.apache.ignite.internal.processors.subscription.GridInternalSubscriptionProcessor;
6464
import org.apache.ignite.internal.processors.timeout.GridTimeoutProcessor;
6565
import org.apache.ignite.internal.util.IgniteUtils;
66+
import org.apache.ignite.migrationtools.persistence.marshallers.ForeignJdkMarshaller;
6667
import org.apache.ignite.spi.deployment.local.LocalDeploymentSpi;
6768
import org.apache.ignite.spi.encryption.noop.NoopEncryptionSpi;
6869
import org.apache.ignite.spi.eventstorage.NoopEventStorageSpi;
@@ -82,6 +83,8 @@ public class MigrationKernalContext extends GridKernalContextImpl {
8283

8384
private static final Field MARSH_CTX_FIELD;
8485

86+
private static final Field JDK_MARSHALLER_FIELD;
87+
8588
static {
8689
try {
8790
CFG_FIELD = GridKernalContextImpl.class.getDeclaredField("cfg");
@@ -92,6 +95,9 @@ public class MigrationKernalContext extends GridKernalContextImpl {
9295

9396
MARSH_CTX_FIELD = GridKernalContextImpl.class.getDeclaredField("marshCtx");
9497
MARSH_CTX_FIELD.setAccessible(true);
98+
99+
JDK_MARSHALLER_FIELD = MarshallerContextImpl.class.getDeclaredField("jdkMarsh");
100+
JDK_MARSHALLER_FIELD.setAccessible(true);
95101
} catch (NoSuchFieldException e) {
96102
throw new RuntimeException(e);
97103
}
@@ -127,6 +133,7 @@ public MigrationKernalContext(IgniteConfiguration cfg, File nodeFolder, Serializ
127133

128134
try {
129135
CFG_FIELD.set(this, adaptedConfiguration);
136+
JDK_MARSHALLER_FIELD.set(marshCtx, new ForeignJdkMarshaller());
130137
MARSH_CTX_FIELD.set(this, marshCtx);
131138

132139
// Unnecessarily required by CacheObjectBinaryProcessorImpl & by GridCacheDefaultAffinityKeyMapper#ignite
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.ignite.migrationtools.persistence.marshallers;
19+
20+
import java.io.InputStream;
21+
import org.apache.ignite.IgniteCheckedException;
22+
import org.apache.ignite.marshaller.jdk.JdkMarshaller;
23+
import org.jetbrains.annotations.Nullable;
24+
25+
/** {@link JdkMarshaller} implementation which uses {@link ForeignObjectInputStream}. */
26+
public class ForeignJdkMarshaller extends JdkMarshaller {
27+
@Override
28+
protected <T> T unmarshal0(InputStream in, @Nullable ClassLoader clsLdr) throws IgniteCheckedException {
29+
// Essentially the same impl as JdkMarshaller but with a different underlying ObjectInputStream.
30+
assert in != null;
31+
32+
ClassLoader localClassLoader = (clsLdr != null) ? clsLdr : getClass().getClassLoader();
33+
34+
try (var objIn = new ForeignObjectInputStream(in, localClassLoader)) {
35+
return (T) objIn.readObject();
36+
} catch (ClassNotFoundException e) {
37+
throw new IgniteCheckedException("Failed to find class with given class loader for unmarshalling "
38+
+ "[clsLdr=" + localClassLoader + ", cls=" + e.getMessage() + "]", e);
39+
} catch (Exception e) {
40+
throw new IgniteCheckedException("Failed to deserialize object with given class loader: " + localClassLoader, e);
41+
}
42+
}
43+
}

0 commit comments

Comments
 (0)