diff --git a/gradle.properties b/gradle.properties index fc7b40bcb..c76193120 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=com.marklogic -version=6.4.1 +version=6.5.0 describedName=MarkLogic Java Client API publishUrl=file:../marklogic-java/releases diff --git a/marklogic-client-api-functionaltests/build.gradle b/marklogic-client-api-functionaltests/build.gradle index 8ba5d9041..20326ed5e 100755 --- a/marklogic-client-api-functionaltests/build.gradle +++ b/marklogic-client-api-functionaltests/build.gradle @@ -23,17 +23,16 @@ dependencies { implementation 'org.skyscreamer:jsonassert:1.5.1' implementation 'org.slf4j:slf4j-api:1.7.36' implementation 'commons-io:commons-io:2.11.0' - implementation 'com.squareup.okio:okio:3.4.0' - implementation 'com.squareup.okhttp3:okhttp:4.11.0' - implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.3' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.3' implementation "org.jdom:jdom2:2.0.6.1" - implementation "com.marklogic:ml-app-deployer:4.5.2" + implementation "com.marklogic:ml-app-deployer:4.6.1" - testImplementation 'ch.qos.logback:logback-classic:1.3.5' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testImplementation 'ch.qos.logback:logback-classic:1.3.14' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.xmlunit:xmlunit-legacy:2.9.0' - testImplementation 'org.apache.commons:commons-lang3:3.12.0' + testImplementation 'org.apache.commons:commons-lang3:3.14.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.14' } diff --git a/marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/fastfunctest/datamovement/ExportListenerTest.java b/marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/fastfunctest/datamovement/ExportListenerTest.java index 3d06f7250..f8116047e 100644 --- a/marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/fastfunctest/datamovement/ExportListenerTest.java +++ b/marklogic-client-api-functionaltests/src/test/java/com/marklogic/client/fastfunctest/datamovement/ExportListenerTest.java @@ -423,71 +423,4 @@ public void testPointInTimeQueryNonDeterministicSet() { // Doc count should be zero after both batchers are done. assertEquals(0, dbClient.newServerEval().xquery(query1).eval().next().getNumber().intValue()); } - - /* - * Trigger batch failure by calling incorrect meta data values - */ - - @Test - public void testOnBatchFailure() { - String jsonDoc = "{" + - "\"employees\": [" + - "{ \"firstName\":\"Will\" , \"lastName\":\"Kirkham\" }," + - "{ \"firstName\":\"Hus\" , \"lastName\":\"Wattan\" }," + - "{ \"firstName\":\"Rod\" , \"lastName\":\"Mendez\" }]" + - "}"; - - // Use WriteBatcher to write the files. - WriteBatcher wbatcher = dmManager.newWriteBatcher(); - - wbatcher.withBatchSize(1000); - StringHandle handle = new StringHandle(); - handle.set(jsonDoc); - String uri = null; - - // Insert 100 documents - for (int i = 0; i < 100; i++) { - uri = "lastname" + i + ".json"; - wbatcher.add(uri, handle); - } - wbatcher.flushAndWait(); - - List docExporterList = Collections.synchronizedList(new ArrayList()); - - QueryManager queryMgr = dbClient.newQueryManager(); - StringQueryDefinition querydef = queryMgr.newStringDefinition(); - querydef.setCriteria("Will AND Hus"); - StringBuilder onBatchFailureStr = new StringBuilder(); - - QueryBatcher exportBatcher = dmManager.newQueryBatcher(querydef) - .withBatchSize(50) - .onUrisReady( - new ExportListener() - .withMetadataCategory(DocumentManager.Metadata.METADATAVALUES) - .onDocumentReady(doc -> { - String uriOfDoc = doc.getUri(); - docExporterList.add(uriOfDoc); - } - ) - .onFailure((batch, throwable) -> { - onBatchFailureStr.append("From onBatchFailure QA Exception"); - System.out.println("From onBatchFailure " + throwable.getMessage()); - System.out.println("From onBatchFailure QA Exception"); - } - ) - ) - .onUrisReady(batch -> { - System.out.println("Batch Numer is " + batch.getJobBatchNumber()); - }) - .onQueryFailure(exception -> { - System.out.println("Exceptions thrown from testOnBatchFailure callback onQueryFailure"); - exception.printStackTrace(); - }); - dmManager.startJob(exportBatcher); - - exportBatcher.awaitCompletion(); - - assertTrue(onBatchFailureStr.toString().contains("From onBatchFailure QA Exception"), - "Unexpected exception: " + onBatchFailureStr); - } } diff --git a/marklogic-client-api/build.gradle b/marklogic-client-api/build.gradle index 8c914219b..2b9f37ce1 100644 --- a/marklogic-client-api/build.gradle +++ b/marklogic-client-api/build.gradle @@ -16,29 +16,27 @@ dependencies { implementation 'org.glassfish.jaxb:jaxb-core:2.3.0.1' } - // Forcing usage of 3.4.0 instead of 3.2.0 to address vulnerability - https://security.snyk.io/vuln/SNYK-JAVA-COMSQUAREUPOKIO-5820002 - implementation 'com.squareup.okio:okio:3.4.0' - implementation 'com.squareup.okhttp3:okhttp:4.11.0' - implementation 'com.squareup.okhttp3:logging-interceptor:4.11.0' + implementation 'com.squareup.okhttp3:okhttp:4.12.0' + implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' implementation 'io.github.rburgst:okhttp-digest:2.7' implementation 'com.sun.mail:javax.mail:1.6.2' implementation 'javax.ws.rs:javax.ws.rs-api:2.1.1' implementation 'org.slf4j:slf4j-api:1.7.36' - implementation 'com.fasterxml.jackson.core:jackson-core:2.15.2' - implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.2' - implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.2' - implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.15.2' + implementation 'com.fasterxml.jackson.core:jackson-core:2.15.3' + implementation 'com.fasterxml.jackson.core:jackson-annotations:2.15.3' + implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.3' + implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-csv:2.15.3' // Only used by extras (which some examples then depend on) // Forcing codec version to avoid vulnerability with older version in httpclient - compileOnly 'commons-codec:commons-codec:1.15' + compileOnly 'commons-codec:commons-codec:1.16.0' compileOnly 'org.apache.httpcomponents:httpclient:4.5.14' compileOnly 'org.jdom:jdom2:2.0.6.1' compileOnly 'org.dom4j:dom4j:2.1.4' compileOnly 'com.google.code.gson:gson:2.10.1' - testImplementation 'org.junit.jupiter:junit-jupiter:5.9.3' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' // Forcing junit version to avoid vulnerability with older version in xmlunit testImplementation 'junit:junit:4.13.2' testImplementation 'org.xmlunit:xmlunit-legacy:2.9.1' @@ -46,19 +44,18 @@ dependencies { // Allows talking to the Manage API. It depends on the Java Client itself, which will usually be a slightly older // version, but that should not have any impact on the tests. - testImplementation "com.marklogic:ml-app-deployer:4.5.2" + testImplementation "com.marklogic:ml-app-deployer:4.6.1" // Starting with mockito 5.x, Java 11 is required, so sticking with 4.x as we have to support Java 8. testImplementation "org.mockito:mockito-core:4.11.0" testImplementation "org.mockito:mockito-inline:4.11.0" - testImplementation 'com.squareup.okio:okio:3.4.0' - testImplementation "com.squareup.okhttp3:mockwebserver:4.11.0" + testImplementation "com.squareup.okhttp3:mockwebserver:4.12.0" - testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.2' - testImplementation 'ch.qos.logback:logback-classic:1.3.5' + testImplementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.15.3' + testImplementation 'ch.qos.logback:logback-classic:1.3.14' // schema validation issue with testImplementation 'xerces:xercesImpl:2.12.0' testImplementation 'org.opengis.cite.xerces:xercesImpl-xsd11:2.12-beta-r1667115' - testImplementation 'org.apache.commons:commons-lang3:3.12.0' + testImplementation 'org.apache.commons:commons-lang3:3.14.0' testImplementation 'org.apache.httpcomponents:httpclient:4.5.14' testImplementation 'com.opencsv:opencsv:4.6' testImplementation 'org.geonames:geonames:1.0' diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java index 219a59ab6..0bf7f5561 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientBuilder.java @@ -301,6 +301,49 @@ public DatabaseClientBuilder withKeyStoreAlgorithm(String algorithm) { props.put(PREFIX + "ssl.keystore.algorithm", algorithm); return this; } + + /** + * Supports constructing an {@code X509TrustManager} based on the given file path, which should point to a Java + * key store or trust store. + * + * @param path + * @return + * @since 6.5.0 + */ + public DatabaseClientBuilder withTrustStorePath(String path) { + props.put(PREFIX + "ssl.truststore.path", path); + return this; + } + + /** + * @param password optional password for a trust store + * @return + * @since 6.5.0 + */ + public DatabaseClientBuilder withTrustStorePassword(String password) { + props.put(PREFIX + "ssl.truststore.password", password); + return this; + } + + /** + * @param type e.g. "JKS" + * @return + * @since 6.5.0 + */ + public DatabaseClientBuilder withTrustStoreType(String type) { + props.put(PREFIX + "ssl.truststore.type", type); + return this; + } + + /** + * @param algorithm e.g. "SunX509" + * @return + * @since 6.5.0 + */ + public DatabaseClientBuilder withTrustStoreAlgorithm(String algorithm) { + props.put(PREFIX + "ssl.truststore.algorithm", algorithm); + return this; + } } diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java index 0f01216c6..d158c2580 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/DatabaseClientFactory.java @@ -1303,6 +1303,10 @@ public String getCertificatePassword() { *
  • marklogic.client.ssl.keystore.password = must be a String; optional password for a key store; since 6.4.0.
  • *
  • marklogic.client.ssl.keystore.type = must be a String; optional type for a key store, defaults to "JKS"; since 6.4.0.
  • *
  • marklogic.client.ssl.keystore.algorithm = must be a String; optional algorithm for a key store, defaults to "SunX509"; since 6.4.0.
  • + *
  • marklogic.client.ssl.truststore.path = must be a String; specifies a file path for a trust store for SSL and/or certificate authentication; since 6.5.0.
  • + *
  • marklogic.client.ssl.truststore.password = must be a String; optional password for a trust store; since 6.5.0.
  • + *
  • marklogic.client.ssl.truststore.type = must be a String; optional type for a trust store, defaults to "JKS"; since 6.5.0.
  • + *
  • marklogic.client.ssl.truststore.algorithm = must be a String; optional algorithm for a trust store, defaults to "SunX509"; since 6.5.0.
  • * * * @param propertySource diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java index b36f0b8aa..8047f93bc 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/DatabaseClientPropertySource.java @@ -23,6 +23,7 @@ import javax.net.ssl.SSLContext; import javax.net.ssl.X509TrustManager; import java.security.KeyManagementException; +import java.security.KeyStore; import java.security.NoSuchAlgorithmException; import java.util.LinkedHashMap; import java.util.Map; @@ -317,9 +318,29 @@ private X509TrustManager getTrustManager() { throw new IllegalArgumentException("Trust manager must be an instanceof " + X509TrustManager.class.getName()); } } + + String path = getNullableStringValue("ssl.truststore.path"); + if (path != null && path.trim().length() > 0) { + return buildTrustManagerFromTrustStorePath(path); + } + return null; } + /** + * Added in 6.5.0 to support configuring a trust manager via properties. + * + * @param path + * @return + */ + private X509TrustManager buildTrustManagerFromTrustStorePath(String path) { + final String password = getNullableStringValue("ssl.truststore.password"); + final String type = getNullableStringValue("ssl.truststore.type", "JKS"); + final String algorithm = getNullableStringValue("ssl.truststore.algorithm", "SunX509"); + KeyStore trustStore = SSLUtil.getKeyStore(path, password != null ? password.toCharArray() : null, type); + return (X509TrustManager) SSLUtil.getTrustManagers(algorithm, trustStore)[0]; + } + private SSLContext getSSLContext() { Object val = propertySource.apply(PREFIX + "sslContext"); if (val != null) { diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/RowManagerImpl.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/RowManagerImpl.java index b85ba6b80..e9d60150b 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/RowManagerImpl.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/RowManagerImpl.java @@ -215,9 +215,7 @@ public void execute(Plan plan) { @Override public void execute(Plan plan, Transaction transaction) { PlanBuilderBaseImpl.RequestPlan requestPlan = checkPlan(plan); - RequestParameters params = newRowsParamsBuilder(requestPlan) - .withOutput("execute") - .getRequestParameters(); + RequestParameters params = newRowsParamsBuilder(requestPlan).getRequestParameters(); RESTServiceResultIterator iter = submitPlan(requestPlan, params, transaction); if (iter != null) { iter.close(); diff --git a/marklogic-client-api/src/main/java/com/marklogic/client/impl/SSLUtil.java b/marklogic-client-api/src/main/java/com/marklogic/client/impl/SSLUtil.java index 099b51c38..00b15bc79 100644 --- a/marklogic-client-api/src/main/java/com/marklogic/client/impl/SSLUtil.java +++ b/marklogic-client-api/src/main/java/com/marklogic/client/impl/SSLUtil.java @@ -60,7 +60,7 @@ public static TrustManager[] getDefaultTrustManagers() { * @param trustManagerAlgorithm e.g. "SunX509". * @param optionalKeyStore if not null, used to initialize the TrustManagerFactory constructed based on the * given algorithm. - * @return + * @return an array of at least length 1 where the first instance is an {@code X509TrustManager} */ public static TrustManager[] getTrustManagers(String trustManagerAlgorithm, KeyStore optionalKeyStore) { TrustManagerFactory trustManagerFactory; diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherFailureTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherFailureTest.java index a24f0485a..dd2f34025 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherFailureTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherFailureTest.java @@ -27,7 +27,7 @@ void invalidQuery() { RowManager rowManager = client.newRowManager(); PlanBuilder op = rowManager.newPlanBuilder(); PlanBuilder.ModifyPlan plan = op - .fromView("opticUnitTest", "musician") + .fromView("opticUnitTest", "musician_ml10") .where(op.eq(op.col("dob"), op.xs.string("this is not a valid date"))); List returnedRows = new ArrayList<>(); diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherTest.java index 87751fc58..2d7915586 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/datamovement/RowBatcherTest.java @@ -43,10 +43,7 @@ import java.io.BufferedReader; import java.io.StringReader; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; +import java.util.*; import java.util.concurrent.ConcurrentSkipListSet; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -203,6 +200,24 @@ public void testJsonRowsForest2Threads() throws Exception { public void testJsonDocs1Thread() throws Exception { runDocsTest(jsonBatcher(1)); } + + @Test + void noRowsReturned() { + RowBatcher rowBatcher = jsonBatcher(1); + RowManager rowMgr = rowBatcher.getRowManager(); + RawQueryDSLPlan plan = rowMgr.newRawQueryDSLPlan( + new StringHandle("op.fromView('rowBatcherUnitTest', 'code').where(op.eq(op.col('field1'), 12345))")); + + List results = new ArrayList<>(); + rowBatcher.withBatchView(plan).onSuccess(batch -> results.add(batch.getRowsDoc())); + moveMgr.startJob(rowBatcher); + rowBatcher.awaitCompletion(); + moveMgr.stopJob(rowBatcher); + + assertEquals(0, results.size(), "Expecting no results as the Optic query shouldn't match any rows; " + + "also expecting no error to occur."); + } + @Test public void testJsonDocs3Threads() throws Exception { runDocsTest(jsonBatcher(3)); diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/RowManagerTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/RowManagerTest.java index 060c42af2..cefa52c30 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/RowManagerTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/rows/RowManagerTest.java @@ -481,24 +481,30 @@ private void testViewRows(RowSet rows) { assertEquals( 2, rowNum); } - @Test - public void testSQL() { - RowManager rowMgr = Common.client.newRowManager(); - PlanBuilder p = rowMgr.newPlanBuilder(); - PlanBuilder.ExportablePlan builtPlan = - p.fromSql("select * from opticUnitTest.musician_ml10"); - int rowNum = 0; - String exception = ""; - try { - for (RowRecord row: rowMgr.resultRows(builtPlan)) { - rowNum++; - } - } catch (Exception e) { - exception = e.toString(); - } - assertEquals(4, rowNum); - assertEquals("", exception); - } + @Test + void testSQL() { + final String query = "select * from opticUnitTest.musician_ml10"; + RowManager mgr = Common.client.newRowManager(); + + RowSet rows = mgr.resultRows(mgr.newPlanBuilder().fromSql(query)); + assertEquals(4, rows.stream().count()); + + JsonNode doc = mgr.resultDoc(mgr.newPlanBuilder().fromSql(query), new JacksonHandle()).get(); + assertEquals(3, doc.get("columns").size()); + assertEquals(4, doc.get("rows").size()); + } + + @Test + void sqlNoRows() { + final String query = "select * from opticUnitTest.musician_ml10 where lastName = 'NOT_FOUND'"; + RowManager mgr = Common.client.newRowManager(); + + RowSet rows = mgr.resultRows(mgr.newPlanBuilder().fromSql(query)); + assertEquals(0, rows.stream().count()); + + JsonNode doc = mgr.resultDoc(mgr.newPlanBuilder().fromSql(query), new JacksonHandle()).get(); + assertNull(doc); + } @Test public void testSQL0Result() { diff --git a/marklogic-client-api/src/test/java/com/marklogic/client/test/ssl/TwoWaySSLTest.java b/marklogic-client-api/src/test/java/com/marklogic/client/test/ssl/TwoWaySSLTest.java index 56274cff0..579d8b8d8 100644 --- a/marklogic-client-api/src/test/java/com/marklogic/client/test/ssl/TwoWaySSLTest.java +++ b/marklogic-client-api/src/test/java/com/marklogic/client/test/ssl/TwoWaySSLTest.java @@ -12,6 +12,7 @@ import com.marklogic.client.test.junit5.RequireSSLExtension; import com.marklogic.mgmt.ManageClient; import com.marklogic.mgmt.resource.appservers.ServerManager; +import com.marklogic.mgmt.resource.security.CertificateTemplateManager; import com.marklogic.rest.util.Fragment; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.BeforeAll; @@ -47,6 +48,7 @@ public class TwoWaySSLTest { private static DatabaseClient securityClient; private static ManageClient manageClient; private static File keyStoreFile; + private static File trustStoreFile; private static File p12File; @@ -72,8 +74,10 @@ public static void setup() throws Exception { writeClientCertificateFilesToTempDir(clientCertificate, tempDir); createPkcs12File(tempDir); createKeystoreFile(tempDir); - keyStoreFile = new File(tempDir.toFile(), "client.jks"); + keyStoreFile = new File(tempDir.toFile(), "keyStore.jks"); + trustStoreFile = new File(tempDir.toFile(), "trustStore.jks"); p12File = new File(tempDir.toFile(), "client.p12"); + addServerCertificateToTrustStore(tempDir); } @AfterAll @@ -88,6 +92,12 @@ public static void teardown() { /** * After two-way SSL is configured on the java-unittest app server, verify that a DatabaseClient using a proper * SSLContext can connect to the app server. + * + * This test can be used for manual testing of two-way SSL - e.g. for ml-gradle - by doing the following: + * - Add a breakpoint at the start of the test. + * - Run the test in a debugger. + * - When the breakpoint is hit, look for the location of the files in stdout. + * - Copy those files to a more accessible location and use them for accessing the 8012 app server. */ @Test void digestAuthentication() { @@ -99,11 +109,15 @@ void digestAuthentication() { DatabaseClient clientWithCert = Common.newClientBuilder() .withKeyStorePath(keyStoreFile.getAbsolutePath()) .withKeyStorePassword(KEYSTORE_PASSWORD) + // Still need this as "common"/"strict" don't work for our temporary server certificate. .withSSLHostnameVerifier(DatabaseClientFactory.SSLHostnameVerifier.ANY) - // This is a reasonable trust manager since it references the temporary server certificate as something - // that it accepts instead of accepting everything. - .withTrustManager(RequireSSLExtension.newSecureTrustManager()) + + // Starting in 6.5.0, we can use a real trust manager as the server certificate is in the keystore. + .withTrustStorePath(trustStoreFile.getAbsolutePath()) + .withTrustStorePassword(KEYSTORE_PASSWORD) + .withTrustStoreType("JKS") + .withTrustStoreAlgorithm("SunX509") .build(); verifyTestDocumentCanBeRead(clientWithCert); @@ -416,11 +430,7 @@ private static void createPkcs12File(Path tempDir) throws Exception { "-name", "my-client", "-passout", "pass:" + KEYSTORE_PASSWORD); - ExecutorService executorService = Executors.newSingleThreadExecutor(); - Process process = builder.start(); - executorService.submit(new StreamGobbler(process.getInputStream(), System.out::println)); - executorService.submit(new StreamGobbler(process.getErrorStream(), System.err::println)); - int exitCode = process.waitFor(); + int exitCode = runProcess(builder); assertEquals(0, exitCode, "Unable to create pkcs12 file using openssl"); } @@ -430,18 +440,49 @@ private static void createKeystoreFile(Path tempDir) throws Exception { builder.command("keytool", "-importkeystore", "-deststorepass", KEYSTORE_PASSWORD, "-destkeypass", KEYSTORE_PASSWORD, - "-destkeystore", "client.jks", + "-destkeystore", "keyStore.jks", "-srckeystore", "client.p12", "-srcstoretype", "PKCS12", "-srcstorepass", KEYSTORE_PASSWORD, "-alias", "my-client"); + int exitCode = runProcess(builder); + assertEquals(0, exitCode, "Unable to create keystore using keytool"); + } + + /** + * Retrieves the server certificate associated with the certificate template for this test and stores it in the + * key store so that the key store can also act as a trust store. + * + * @param tempDir + * @throws Exception + */ + private static void addServerCertificateToTrustStore(Path tempDir) throws Exception { + Fragment xml = new CertificateTemplateManager(Common.newManageClient()).getCertificatesForTemplate("java-unittest-template"); + String serverCertificate = xml.getElementValue("/msec:certificate-list/msec:certificate/msec:pem"); + + File certificateFile = new File(tempDir.toFile(), "server.cert"); + FileCopyUtils.copy(serverCertificate.getBytes(), certificateFile); + + ProcessBuilder builder = new ProcessBuilder(); + builder.directory(tempDir.toFile()); + builder.command("keytool", "-importcert", + "-keystore", trustStoreFile.getAbsolutePath(), + "-storepass", KEYSTORE_PASSWORD, + "-file", certificateFile.getAbsolutePath(), + "-noprompt", + "-alias", "java-unittest-template-certificate"); + + int exitCode = runProcess(builder); + assertEquals(0, exitCode, "Unable to add server public certificate to keystore."); + } + + private static int runProcess(ProcessBuilder builder) throws Exception { Process process = builder.start(); ExecutorService executorService = Executors.newSingleThreadExecutor(); executorService.submit(new StreamGobbler(process.getInputStream(), System.out::println)); executorService.submit(new StreamGobbler(process.getErrorStream(), System.err::println)); - int exitCode = process.waitFor(); - assertEquals(0, exitCode, "Unable to create keystore using keytool"); + return process.waitFor(); } /** diff --git a/ml-development-tools/build.gradle b/ml-development-tools/build.gradle index 456579b04..a4dbdba8b 100644 --- a/ml-development-tools/build.gradle +++ b/ml-development-tools/build.gradle @@ -12,8 +12,8 @@ dependencies { compileOnly gradleApi() implementation project(':marklogic-client-api') implementation 'org.jetbrains.kotlin:kotlin-stdlib:1.8.22' - implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.2' - implementation 'com.networknt:json-schema-validator:1.0.86' + implementation 'com.fasterxml.jackson.module:jackson-module-kotlin:2.15.3' + implementation 'com.networknt:json-schema-validator:1.0.88' // Not yet migrating this project to JUnit 5. Will reconsider it once we have a reason to enhance // this project. @@ -21,9 +21,7 @@ dependencies { testImplementation 'xmlunit:xmlunit:1.6' testCompileOnly gradleTestKit() - // Forcing usage of 3.4.0 instead of 3.2.0 to address vulnerability - https://security.snyk.io/vuln/SNYK-JAVA-COMSQUAREUPOKIO-5820002 - testImplementation 'com.squareup.okio:okio:3.4.0' - testImplementation 'com.squareup.okhttp3:okhttp:4.11.0' + testImplementation 'com.squareup.okhttp3:okhttp:4.12.0' } // Added to avoid problem where processResources fails because - somehow - the plugin properties file is getting diff --git a/test-app/build.gradle b/test-app/build.gradle index 2bce530d0..bab083b82 100644 --- a/test-app/build.gradle +++ b/test-app/build.gradle @@ -1,5 +1,5 @@ plugins { - id 'com.marklogic.ml-gradle' version '4.5.2' + id 'com.marklogic.ml-gradle' version '4.6.1' id 'java' id "com.github.psxpaul.execfork" version "0.2.2" } @@ -7,12 +7,11 @@ plugins { dependencies { implementation "io.undertow:undertow-core:2.2.24.Final" implementation "io.undertow:undertow-servlet:2.2.24.Final" - implementation "com.marklogic:ml-javaclient-util:4.5.1" + implementation "com.marklogic:ml-javaclient-util:4.6.0" implementation 'org.slf4j:slf4j-api:1.7.36' - implementation 'ch.qos.logback:logback-classic:1.3.5' - implementation "com.fasterxml.jackson.core:jackson-databind:2.15.2" - implementation 'com.squareup.okio:okio:3.4.0' - implementation 'com.squareup.okhttp3:okhttp:4.11.0' + implementation 'ch.qos.logback:logback-classic:1.3.14' + implementation "com.fasterxml.jackson.core:jackson-databind:2.15.3" + implementation 'com.squareup.okhttp3:okhttp:4.12.0' } // See https://github.com/psxpaul/gradle-execfork-plugin for docs.