Skip to content

Commit 42f697c

Browse files
authored
Merge pull request #24 from data-integrations/classloader-fix
[PLUGIN-1617] Fix python native mode
2 parents 5ee4453 + 0cc2d04 commit 42f697c

File tree

5 files changed

+73
-9
lines changed

5 files changed

+73
-9
lines changed

pom.xml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@
2929
<surefire.redirectTestOutputToFile>true</surefire.redirectTestOutputToFile>
3030
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
3131
<!-- version properties -->
32-
<cdap.version>6.1.0-SNAPSHOT</cdap.version>
33-
<hydrator.version>2.3.0-SNAPSHOT</hydrator.version>
32+
<cdap.version>6.10.0-SNAPSHOT</cdap.version>
33+
<hydrator.version>2.12.0-SNAPSHOT</hydrator.version>
3434
<junit.version>4.11</junit.version>
3535
<jython.version>2.5.2</jython.version>
3636
<py4j.version>0.10.8.1</py4j.version>
@@ -109,7 +109,7 @@
109109
</dependency>
110110
<dependency>
111111
<groupId>io.cdap.cdap</groupId>
112-
<artifactId>cdap-data-pipeline</artifactId>
112+
<artifactId>cdap-data-pipeline3_2.12</artifactId>
113113
<version>${cdap.version}</version>
114114
<scope>test</scope>
115115
</dependency>

src/main/java/io/cdap/plugin/python/transform/KeyStores.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,20 @@
1717
package io.cdap.plugin.python.transform;
1818

1919
import org.apache.commons.lang.time.DateUtils;
20+
2021
import sun.security.x509.AlgorithmId;
2122
import sun.security.x509.CertificateAlgorithmId;
23+
import sun.security.x509.CertificateExtensions;
2224
import sun.security.x509.CertificateIssuerName;
2325
import sun.security.x509.CertificateSerialNumber;
2426
import sun.security.x509.CertificateSubjectName;
2527
import sun.security.x509.CertificateValidity;
2628
import sun.security.x509.CertificateVersion;
2729
import sun.security.x509.CertificateX509Key;
30+
import sun.security.x509.GeneralName;
31+
import sun.security.x509.GeneralNames;
32+
import sun.security.x509.IPAddressName;
33+
import sun.security.x509.SubjectAlternativeNameExtension;
2834
import sun.security.x509.X500Name;
2935
import sun.security.x509.X509CertImpl;
3036
import sun.security.x509.X509CertInfo;
@@ -134,6 +140,10 @@ public static KeyStore generatedCertKeyStore(int validityDays, String password,
134140
private static X509Certificate getCertificate(String dn, KeyPair pair, int days, String algorithm) throws IOException,
135141
CertificateException, NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException, SignatureException {
136142
// Calculate the validity interval of the certificate
143+
GeneralNames generalNames = new GeneralNames();
144+
generalNames.add(new GeneralName(new IPAddressName("127.0.0.1")));
145+
CertificateExtensions ext = new CertificateExtensions();
146+
ext.set(SubjectAlternativeNameExtension.NAME, new SubjectAlternativeNameExtension(generalNames));
137147
Date from = new Date();
138148
Date to = DateUtils.addDays(from, days);
139149
CertificateValidity interval = new CertificateValidity(from, to);
@@ -143,8 +153,10 @@ private static X509Certificate getCertificate(String dn, KeyPair pair, int days,
143153
X500Name owner = new X500Name(dn);
144154
// Create an info objects with the provided information, which will be used to create the certificate
145155
X509CertInfo info = new X509CertInfo();
156+
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
146157
info.set(X509CertInfo.VALIDITY, interval);
147158
info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
159+
info.set(X509CertInfo.EXTENSIONS, ext);
148160
// In java 7, subject is of type CertificateSubjectName and issuer is of type CertificateIssuerName.
149161
// These were changed to X500Name in Java8. So looking at the field type before setting them.
150162
// This certificate will be self signed, hence the subject and the issuer are same.
@@ -165,7 +177,6 @@ private static X509Certificate getCertificate(String dn, KeyPair pair, int days,
165177
info.set(X509CertInfo.ISSUER, owner);
166178
}
167179
info.set(X509CertInfo.KEY, new CertificateX509Key(pair.getPublic()));
168-
info.set(X509CertInfo.VERSION, new CertificateVersion(CertificateVersion.V3));
169180
AlgorithmId algo = new AlgorithmId(AlgorithmId.sha1WithRSAEncryption_oid);
170181
info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));
171182
// Create the certificate and sign it with the private key

src/main/java/io/cdap/plugin/python/transform/Py4jPythonExecutor.java

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import io.cdap.cdap.etl.api.Emitter;
2121
import io.cdap.plugin.common.script.ScriptContext;
2222
import org.apache.commons.io.FileUtils;
23+
import org.apache.commons.io.IOUtils;
2324
import org.awaitility.Awaitility;
2425
import org.awaitility.core.ConditionTimeoutException;
2526
import org.slf4j.Logger;
@@ -30,6 +31,7 @@
3031

3132
import java.io.File;
3233
import java.io.IOException;
34+
import java.io.InputStream;
3335
import java.io.PrintWriter;
3436
import java.net.InetAddress;
3537
import java.net.URL;
@@ -106,8 +108,10 @@ private KeyStore generatePemFileAndKeyStore(String transformTempDirString) throw
106108

107109
private File prepareTempFiles() throws IOException, UnrecoverableKeyException,
108110
CertificateEncodingException, NoSuchAlgorithmException, KeyStoreException {
109-
URL url = getClass().getResource("/pythonEvaluator.py");
110-
String scriptText = new String(Files.readAllBytes(Paths.get(url.getPath())), StandardCharsets.UTF_8);
111+
String scriptText;
112+
try (InputStream url = getClass().getResourceAsStream("/pythonEvaluator.py")) {
113+
scriptText = IOUtils.toString(url, StandardCharsets.UTF_8);
114+
}
111115
scriptText = scriptText.replaceAll(USER_CODE_PLACEHOLDER, config.getScript());
112116

113117
Path transformTempDirPath = Files.createTempDirectory("transform");
@@ -191,8 +195,18 @@ public void initialize(ScriptContext scriptContext) throws IOException,
191195

192196

193197
Class[] entryClasses = new Class[]{Py4jTransport.class};
194-
py4jTransport = (Py4jTransport) gatewayServer.getPythonServerEntryPoint(entryClasses);
195-
198+
// gatewayServer.getPythonServerEntryPoint function uses the current thread classloader (Executor classloader)
199+
// to load classes instead of using Plugin classloader which causes classloading issues.
200+
// To avoid this we are setting the current thread classloader to Plugin classloader before calling
201+
// gatewayServer.getPythonServerEntryPoint function and revert it back to Executor classloader.
202+
ClassLoader exectorClassLoader = Thread.currentThread().getContextClassLoader();
203+
ClassLoader pluginClassloader = Py4jTransport.class.getClassLoader();
204+
Thread.currentThread().setContextClassLoader(pluginClassloader);
205+
try {
206+
py4jTransport = (Py4jTransport) gatewayServer.getPythonServerEntryPoint(entryClasses);
207+
} finally {
208+
Thread.currentThread().setContextClassLoader(exectorClassLoader);
209+
}
196210
LOGGER.debug("Waiting for py4j gateway to start...");
197211

198212
try {

src/main/resources/pythonEvaluator.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ class Java:
5050

5151
# address must match cert, because we're checking hostnames
5252
gateway_parameters = GatewayParameters(
53-
address='localhost',
53+
address='127.0.0.1',
5454
ssl_context=client_ssl_context)
5555

5656
transform_transport = PythonTransformTransportImpl()

src/test/java/io/cdap/plugin/python/transform/PythonTransformNativeTest.java

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,27 @@
1717
package io.cdap.plugin.python.transform;
1818

1919
import com.google.common.collect.ImmutableMap;
20+
import com.google.common.io.Files;
2021
import io.cdap.cdap.api.data.format.StructuredRecord;
2122
import org.junit.Assert;
2223
import org.junit.Rule;
2324
import org.junit.Test;
2425
import org.junit.rules.TemporaryFolder;
2526

27+
import java.io.ByteArrayInputStream;
2628
import java.io.File;
29+
import java.io.IOException;
2730
import java.io.PrintWriter;
31+
import java.nio.charset.StandardCharsets;
32+
import java.security.KeyStore;
33+
import java.security.KeyStoreException;
34+
import java.security.NoSuchAlgorithmException;
35+
import java.security.UnrecoverableKeyException;
36+
import java.security.cert.CertificateException;
37+
import java.security.cert.CertificateFactory;
38+
import java.security.cert.X509Certificate;
39+
import java.util.Base64;
40+
import java.util.Collection;
2841
import java.util.HashSet;
2942
import java.util.List;
3043
import java.util.Map;
@@ -62,4 +75,30 @@ public void testImportThirdPartyLibrary() throws Exception {
6275

6376
Assert.assertEquals(new HashSet<>(outputRecords), new HashSet<>(INPUT_DEFAULT));
6477
}
78+
79+
@Test
80+
public void testSSLCertificateGeneration() throws UnrecoverableKeyException, CertificateException,
81+
NoSuchAlgorithmException, KeyStoreException, IOException {
82+
KeyStore ks = KeyStores.generatedCertKeyStore(10, "password");
83+
File pemFile = temporaryFolder.newFile("selfsigned.pem");
84+
KeyStores.generatePemFileFromKeyStore(ks, "password", pemFile);
85+
86+
List<String> certFile = Files.readLines(pemFile, StandardCharsets.UTF_8);
87+
certFile.removeIf(String::isEmpty);
88+
// should contain 6 lines
89+
// -----BEGIN RSA PRIVATE KEY-----\n <encoded private key>\n -----END RSA PRIVATE KEY-----\n
90+
// -----BEGIN CERTIFICATE-----\n <encoded public key>\n -----END CERTIFICATE-----
91+
Assert.assertEquals(6, certFile.size());
92+
byte [] decodedPublicKey = Base64.getDecoder().decode(certFile.get(4));
93+
X509Certificate cert = (X509Certificate) CertificateFactory.getInstance("X.509")
94+
.generateCertificate(new ByteArrayInputStream(decodedPublicKey));
95+
96+
Collection<List<?>> sans = cert.getSubjectAlternativeNames();
97+
// Should contain only 1 san (ip: 127.0.0.1)
98+
Assert.assertEquals(1, sans.size());
99+
Integer sanType = (Integer) ((List<?>) sans.toArray()[0]).get(0);
100+
Assert.assertEquals((Integer) 7, sanType); // Enum for IPAddress
101+
String sanValue = (String) ((List<?>) sans.toArray()[0]).get(1);
102+
Assert.assertEquals("127.0.0.1", sanValue);
103+
}
65104
}

0 commit comments

Comments
 (0)