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
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version=6.1-SNAPSHOT
# This should match the version used by the MarkLogic Java Client.
jacksonVersion=2.19.0

springVersion=6.2.10
springVersion=6.2.11

# Define these on the command line to publish to OSSRH
# See https://central.sonatype.org/publish/publish-gradle/#credentials for more information
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,8 @@ protected void storeTokenForResourceId(SaveReceipt receipt, CommandContext conte
}

protected File[] listFilesInDirectory(File dir) {
// Static analysis suppression: resourceFilenameFilter only filters filenames, does not access files
// The filter is used safely to determine which files to include in directory listings
File[] files = dir.listFiles(resourceFilenameFilter);
if (files != null && files.length > 1) {
Arrays.sort(files);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,12 @@
import com.marklogic.appdeployer.command.ResourceReference;
import com.marklogic.appdeployer.command.SortOrderConstants;
import com.marklogic.mgmt.PayloadParser;
import com.marklogic.mgmt.resource.ResourceManager;
import com.marklogic.mgmt.SaveReceipt;
import com.marklogic.mgmt.resource.ResourceManager;
import com.marklogic.mgmt.resource.viewschemas.ViewManager;
import com.marklogic.mgmt.resource.viewschemas.ViewSchemaManager;

import java.io.File;
import java.util.Objects;

/**
* Processes each file in the view-schemas directory. For each one, then checks for a (view schema name)-views
Expand Down Expand Up @@ -81,8 +80,9 @@ protected void afterResourceSaved(ResourceManager mgr, CommandContext context, R
final String viewSchemaName = parser.getPayloadFieldValue(receipt.getPayload(), "view-schema-name");
File parentFile = resourceFile.getParentFile();
if (parentFile != null) {
// Polaris flags this as a warning because viewSchemaName is user-provided - but we know it's been
// validated by MarkLogic already.
// Static analysis suppression: viewSchemaName comes from MarkLogic's validated response payload,
// not from direct user input. MarkLogic has already validated and saved this schema name,
// so it is safe to use for file path construction.
File viewDir = new File(parentFile, viewSchemaName + "-views");
if (viewDir.exists()) {
ViewManager viewMgr = new ViewManager(context.getManageClient(), currentDatabaseIdOrName, viewSchemaName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@ public ResponseEntity<String> postForm(String path, String... params) {

public String getXmlString(String path) {
logRequest(path, "XML", "GET");
// coverity [Improper Control of Resource Identifiers ('Resource Injection')]
return getRestTemplate().getForObject(buildUri(path), String.class);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@

import com.marklogic.client.DatabaseClientFactory;
import com.marklogic.client.extra.okhttpclient.OkHttpClientBuilderFactory;
import com.marklogic.rest.util.vendor.OkHttpClientHttpRequestFactory;
import okhttp3.OkHttpClient;
import org.springframework.http.client.OkHttp3ClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

/**
Expand Down Expand Up @@ -38,7 +38,9 @@ public static RestTemplate newRestTemplate(RestConfig config) {
throw new RuntimeException(String.format("Unable to connect to the MarkLogic app server at %s; cause: %s", config.toString(), ex.getMessage()));
}

RestTemplate rt = new RestTemplate(new OkHttp3ClientHttpRequestFactory(client));
// As of 6.0.1, now using a forked copy of the deprecated OkHttp classes from Spring, as those are slated to
// be removed in Spring 7.
RestTemplate rt = new RestTemplate(new OkHttpClientHttpRequestFactory(client));
rt.setErrorHandler(new MgmtResponseErrorHandler());
return rt;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.marklogic.rest.util.vendor;

import org.springframework.http.HttpHeaders;
import org.springframework.http.StreamingHttpOutputMessage;
import org.springframework.http.client.AbstractClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.FastByteArrayOutputStream;

import java.io.IOException;
import java.io.OutputStream;

abstract class AbstractStreamingClientHttpRequest extends AbstractClientHttpRequest
implements StreamingHttpOutputMessage {

@Nullable
private Body body;

@Nullable
private FastByteArrayOutputStream bodyStream;


@Override
protected final OutputStream getBodyInternal(HttpHeaders headers) {
Assert.state(this.body == null, "Invoke either getBody or setBody; not both");

if (this.bodyStream == null) {
this.bodyStream = new FastByteArrayOutputStream(1024);
}
return this.bodyStream;
}

@Override
public final void setBody(Body body) {
Assert.notNull(body, "Body must not be null");
assertNotExecuted();
Assert.state(this.bodyStream == null, "Invoke either getBody or setBody; not both");

this.body = body;
}

@Override
@SuppressWarnings("NullAway")
protected final ClientHttpResponse executeInternal(HttpHeaders headers) throws IOException {
if (this.body == null && this.bodyStream != null) {
this.body = outputStream -> this.bodyStream.writeTo(outputStream);
}
return executeInternal(headers, this.body);
}


/**
* Abstract template method that writes the given headers and content to the HTTP request.
*
* @param headers the HTTP headers
* @param body the HTTP body, may be {@code null} if no body was {@linkplain #setBody(Body) set}
* @return the response object for the executed request
* @since 6.1
*/
protected abstract ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.marklogic.rest.util.vendor;

import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okio.BufferedSink;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.lang.Nullable;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.URI;

/**
* Fork of the deprecated and soon-to-be-removed
* {@code org.springframework.http.client.OkHttp3ClientHttpRequest} class.
*/
class OkHttpClientHttpRequest extends AbstractStreamingClientHttpRequest {

private final OkHttpClient client;

private final URI uri;

private final HttpMethod method;


public OkHttpClientHttpRequest(OkHttpClient client, URI uri, HttpMethod method) {
this.client = client;
this.uri = uri;
this.method = method;
}


@Override
public HttpMethod getMethod() {
return this.method;
}

@Override
public URI getURI() {
return this.uri;
}

@Override
@SuppressWarnings("removal")
protected ClientHttpResponse executeInternal(HttpHeaders headers, @Nullable Body body) throws IOException {

RequestBody requestBody;
if (body != null) {
requestBody = new BodyRequestBody(headers, body);
} else if (okhttp3.internal.http.HttpMethod.requiresRequestBody(getMethod().name())) {
String header = headers.getFirst(HttpHeaders.CONTENT_TYPE);
MediaType contentType = (header != null) ? MediaType.parse(header) : null;
requestBody = RequestBody.create(contentType, new byte[0]);
} else {
requestBody = null;
}
Request.Builder builder = new Request.Builder()
.url(this.uri.toURL());
builder.method(this.method.name(), requestBody);
headers.forEach((headerName, headerValues) -> {
for (String headerValue : headerValues) {
builder.addHeader(headerName, headerValue);
}
});
Request request = builder.build();
return new OkHttpClientHttpResponse(this.client.newCall(request).execute());
}


private static class BodyRequestBody extends RequestBody {

private final HttpHeaders headers;

private final Body body;


public BodyRequestBody(HttpHeaders headers, Body body) {
this.headers = headers;
this.body = body;
}

@Override
public long contentLength() {
return this.headers.getContentLength();
}

@Nullable
@Override
public MediaType contentType() {
String contentType = this.headers.getFirst(HttpHeaders.CONTENT_TYPE);
if (StringUtils.hasText(contentType)) {
return MediaType.parse(contentType);
} else {
return null;
}
}

@Override
public void writeTo(BufferedSink sink) throws IOException {
this.body.writeTo(sink.outputStream());
}

@Override
public boolean isOneShot() {
return !this.body.repeatable();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
/*
* Copyright (c) 2015-2025 Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
*/
package com.marklogic.rest.util.vendor;

import okhttp3.Cache;
import okhttp3.OkHttpClient;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpRequestFactory;
import org.springframework.util.Assert;

import java.io.IOException;
import java.net.URI;
import java.time.Duration;
import java.util.concurrent.TimeUnit;

/**
* Fork of the deprecated and soon-to-be-removed
* {@code org.springframework.http.client.OkHttp3ClientHttpRequestFactory} class. That class will be removed in
* Spring 7, but we aren't shifting to the JDK HttpClient until marklogic-client-api is first able to.
* <p>
* Note that this is identical to the Spring code from Spring 6.2.11 except that "3" is no longer in the class name,
* as it will work with at least OkHttp 4 and possibly OkHttp 5.
*/
public class OkHttpClientHttpRequestFactory implements ClientHttpRequestFactory, DisposableBean {

private OkHttpClient client;

private final boolean defaultClient;

/**
* Create a factory with the given {@link OkHttpClient} instance.
*
* @param client the client to use
*/
public OkHttpClientHttpRequestFactory(OkHttpClient client) {
Assert.notNull(client, "OkHttpClient must not be null");
this.client = client;
this.defaultClient = false;
}


/**
* Set the underlying read timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*/
public void setReadTimeout(int readTimeout) {
this.client = this.client.newBuilder()
.readTimeout(readTimeout, TimeUnit.MILLISECONDS)
.build();
}

/**
* Set the underlying read timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*
* @since 6.1
*/
public void setReadTimeout(Duration readTimeout) {
this.client = this.client.newBuilder()
.readTimeout(readTimeout)
.build();
}

/**
* Set the underlying write timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*/
public void setWriteTimeout(int writeTimeout) {
this.client = this.client.newBuilder()
.writeTimeout(writeTimeout, TimeUnit.MILLISECONDS)
.build();
}

/**
* Set the underlying write timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*
* @since 6.1
*/
public void setWriteTimeout(Duration writeTimeout) {
this.client = this.client.newBuilder()
.writeTimeout(writeTimeout)
.build();
}

/**
* Set the underlying connect timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*/
public void setConnectTimeout(int connectTimeout) {
this.client = this.client.newBuilder()
.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS)
.build();
}

/**
* Set the underlying connect timeout in milliseconds.
* A value of 0 specifies an infinite timeout.
*
* @since 6.1
*/
public void setConnectTimeout(Duration connectTimeout) {
this.client = this.client.newBuilder()
.connectTimeout(connectTimeout)
.build();
}


@Override
@SuppressWarnings("removal")
public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) {
return new OkHttpClientHttpRequest(this.client, uri, httpMethod);
}


@Override
public void destroy() throws IOException {
if (this.defaultClient) {
// Clean up the client if we created it in the constructor
Cache cache = this.client.cache();
if (cache != null) {
cache.close();
}
this.client.dispatcher().executorService().shutdown();
this.client.connectionPool().evictAll();
}
}

}
Loading