Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,6 @@ subprojects {

repositories {
mavenCentral()

mavenLocal()

// Temporarily using to depend on marklogic-junit5 snapshot until it's released.
maven {
url = "https://bed-artifactory.bedford.progress.com:443/artifactory/ml-maven-snapshots/"
}
}

dependencies {
Expand Down
20 changes: 12 additions & 8 deletions ml-app-deployer/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ dependencies {
api "com.fasterxml.jackson.core:jackson-databind:${jacksonVersion}"

// For compiling JAXB code; marklogic-client-api brings this in at runtime.
compileOnly "jakarta.xml.bind:jakarta.xml.bind-api:4.0.4"
compileOnly "jakarta.xml.bind:jakarta.xml.bind-api:4.0.5"

// For resolving XPath expressions against XML documents.
implementation 'jaxen:jaxen:2.0.0'
Expand All @@ -21,25 +21,29 @@ dependencies {
implementation 'org.jdom:jdom2:2.0.6.1'

// For EqualsBuilder; added in 3.8.1 to support detecting if a mimetype's properties have changed or not
implementation "org.apache.commons:commons-lang3:3.19.0"

testImplementation 'org.junit-pioneer:junit-pioneer:2.3.0'
implementation "org.apache.commons:commons-lang3:3.20.0"

// For PreviewInterceptor; can be excluded if that feature is not used
implementation("com.flipkart.zjsonpatch:zjsonpatch:0.4.16") {
// Prefer the api version declared above
exclude module: "jackson-databind"
}

implementation "com.progress:pdc-java-client:1.0-SNAPSHOT"
// Required for the pdc-java-client code that has been temporarily added to this subproject
// for at least the 6.2.0 release.
implementation 'com.squareup.okhttp3:logging-interceptor:5.2.0'
implementation 'com.google.code.gson:gson:2.13.2'
implementation 'io.gsonfire:gson-fire:1.9.0'
implementation 'org.openapitools:jackson-databind-nullable:0.2.8'
implementation 'jakarta.annotation:jakarta.annotation-api:3.0.0'

// Need this for compiling tests too.
testCompileOnly "jakarta.xml.bind:jakarta.xml.bind-api:4.0.4"
testCompileOnly "jakarta.xml.bind:jakarta.xml.bind-api:4.0.5"

testImplementation 'commons-io:commons-io:2.19.0'
testImplementation 'commons-io:commons-io:2.21.0'
testImplementation 'org.xmlunit:xmlunit-legacy:2.10.4'

testImplementation 'org.mockito:mockito-core:5.20.0'
testImplementation 'org.mockito:mockito-core:5.23.0'

// Gradle will alter this to use the ml-javaclient-util project in this repository.
testImplementation "com.marklogic:marklogic-junit5:2.0.0"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/*
* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.progress.pdc.client;

import com.marklogic.client.DatabaseClient;
import com.marklogic.client.DatabaseClientBuilder;
import com.progress.pdc.client.generated.ApiClient;
import com.progress.pdc.client.generated.ApiException;
import com.progress.pdc.client.generated.JSON;
import com.progress.pdc.client.generated.api.ServiceGroupApi;
import com.progress.pdc.client.generated.model.ServiceGroupViewModel;
import com.progress.pdc.client.impl.GsonUtil;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;

import java.io.Closeable;
import java.util.Objects;
import java.util.UUID;

public class PdcClient implements Closeable {

public static Builder newBuilder(String host, String apiKey) {
return new Builder(host, apiKey);
}

public static class Builder {
private final String host;
private final String apiKey;
private Interceptor okHttpInterceptor;

public Builder(String host, String apiKey) {
this.host = host;
this.apiKey = apiKey;
}

public Builder okHttpInterceptor(Interceptor interceptor) {
this.okHttpInterceptor = interceptor;
return this;
}

public PdcClient build() {
return new PdcClient(host, apiKey, okHttpInterceptor);
}
}

private final ApiClient apiClient;
private final DatabaseClient databaseClient;
private final Interceptor okHttpInterceptor;

private PdcClient(String host, String apiKey, Interceptor okHttpInterceptor) {
DatabaseClient databaseClient = new DatabaseClientBuilder()
.withHost(host)
.withCloudAuth(apiKey, null)
.build();

OkHttpClient okHttpClient = (OkHttpClient) databaseClient.getClientImplementation();
Objects.requireNonNull(okHttpClient, "OkHttpClient implementation expected from DatabaseClient");
if (okHttpInterceptor != null) {
okHttpClient = okHttpClient.newBuilder()
.addInterceptor(okHttpInterceptor)
.build();
}

ApiClient apiClient = new ApiClient(okHttpClient);
apiClient.setBasePath("https://%s".formatted(databaseClient.getHost()));

this.databaseClient = databaseClient;
this.apiClient = apiClient;
this.okHttpInterceptor = okHttpInterceptor;
JSON.setGson(GsonUtil.createGson());
}

public UUID getEnvironmentId() {
try {
ServiceGroupViewModel viewModel = new ServiceGroupApi(apiClient).apiServicegroupGet(null).get(0);
return viewModel.getId();
} catch (ApiException ex) {
throw new PdcClientException("Unable to get environment ID, could not get service groups from PDC", ex);
}
}

public String getHost() {
return this.databaseClient.getHost();
}

public ApiClient getApiClient() {
return apiClient;
}

@Override
public void close() {
this.databaseClient.release();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/*
* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.progress.pdc.client;

import com.progress.pdc.client.generated.ApiException;

/**
* Intended to provide additional context without losing the original ApiException.
*/
public class PdcClientException extends RuntimeException {

public PdcClientException(String message, ApiException cause) {
super(message, cause);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.progress.pdc.client;

import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;

import java.io.IOException;

/**
* Addresses at least one issue where after a MarkLogic endpoint is created in PDC, subsequent calls within the
* next second or two will receive a 404, possibly due to a load balancer restart.
*/
public record RetryOn404Interceptor(int maxRetries, long retryDelayMs) implements Interceptor {

@Override
public Response intercept(Chain chain) throws IOException {
final Request request = chain.request();
Response response = chain.proceed(request);
int retryCount = 0;

while (response.code() == 404 && retryCount < maxRetries) {
Util.LOGGER.debug("Received 404 for request to {}, retrying {}/{} after {} ms",
request.url(), retryCount + 1, maxRetries, retryDelayMs);
response.close();
retryCount++;

try {
Thread.sleep(retryDelayMs);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new IOException("Retry interrupted", e);
}

response = chain.proceed(request);
}

return response;
}
}
12 changes: 12 additions & 0 deletions ml-app-deployer/src/main/java/com/progress/pdc/client/Util.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/*
* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.progress.pdc.client;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public interface Util {

Logger LOGGER = LoggerFactory.getLogger("com.progress.pdc.client");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* Copyright (c) 2015-2026 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*
* NOTE: This class is auto generated by OpenAPI Generator (https://openapi-generator.tech).
* https://openapi-generator.tech
* Do not edit the class manually.
*/


package com.progress.pdc.client.generated;


import java.util.List;
import java.util.Map;

/**
* Callback for asynchronous API call.
*
* @param <T> The return type
*/
public interface ApiCallback<T> {
/**
* This is called when the API call fails.
*
* @param e The exception causing the failure
* @param statusCode Status code of the response if available, otherwise it would be 0
* @param responseHeaders Headers of the response if available, otherwise it would be null
*/
void onFailure(ApiException e, int statusCode, Map<String, List<String>> responseHeaders);

/**
* This is called when the API call succeeded.
*
* @param result The result deserialized from response
* @param statusCode Status code of the response
* @param responseHeaders Headers of the response
*/
void onSuccess(T result, int statusCode, Map<String, List<String>> responseHeaders);

/**
* This is called when the API upload processing.
*
* @param bytesWritten bytes Written
* @param contentLength content length of request body
* @param done write end
*/
void onUploadProgress(long bytesWritten, long contentLength, boolean done);

/**
* This is called when the API download processing.
*
* @param bytesRead bytes Read
* @param contentLength content length of the response
* @param done Read end
*/
void onDownloadProgress(long bytesRead, long contentLength, boolean done);
}
Loading
Loading