All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.marklogic.mgmt.ManageClient Maven / Gradle / Ivy

Go to download

Java client for the MarkLogic REST Management API and for deploying applications to MarkLogic

There is a newer version: 5.0.0
Show newest version
/*
 * Copyright (c) 2023 MarkLogic Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.marklogic.mgmt;

import com.fasterxml.jackson.databind.JsonNode;
import com.marklogic.client.ext.helper.LoggingObject;
import com.marklogic.mgmt.util.ObjectMapperFactory;
import com.marklogic.rest.util.Fragment;
import com.marklogic.rest.util.RestConfig;
import com.marklogic.rest.util.RestTemplateUtil;
import org.jdom2.Namespace;
import org.springframework.http.*;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StringUtils;
import org.springframework.web.client.RestTemplate;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;

/**
 * Wraps a RestTemplate with methods that should simplify accessing the Manage API with RestTemplate. Each NounManager
 * should depend on an instance of ManageClient for accessing the Manage API.
 */
public class ManageClient extends LoggingObject {

	private ManageConfig manageConfig;
	private RestTemplate restTemplate;
	private RestTemplate securityUserRestTemplate;
	private PayloadParser payloadParser;

	/**
	 * Creates an uninitialized instance that requires a {@code ManageConfig} to be provided in order to be operable.
	 *
	 * @deprecated since 4.5.0; will be removed in 5.0.0
	 */
    public ManageClient() {
    }

    public ManageClient(ManageConfig config) {
        setManageConfig(config);
    }

	/**
	 * Uses the given ManageConfig instance to construct a Spring RestTemplate for communicating with the Manage API.
	 * In addition, if adminUsername on the ManageConfig instance differs from username, then a separate RestTemplate is
	 * constructed for making calls to the Manage API that need user with the manage-admin and security roles, which is
	 * often an admin user.
	 *
	 * @param config
	 */
	public void setManageConfig(ManageConfig config) {
	    this.manageConfig = config;
	    if (logger.isInfoEnabled()) {
		    logger.info("Initializing ManageClient with manage config of: " + config);
	    }
    }

	/**
	 * Use this when you want to provide your own RestTemplate as opposed to using the one that's constructed via a
	 * ManageConfig instance.
	 *
	 * @param restTemplate
	 */
	public ManageClient(RestTemplate restTemplate) {
    	this(restTemplate, restTemplate);
    }

	/**
	 * Use this when you want to provide your own RestTemplate as opposed to using the one that's constructed via a
	 * ManageConfig instance.
	 *
	 * @param restTemplate
	 * @param adminRestTemplate
	 */
	public ManageClient(RestTemplate restTemplate, RestTemplate adminRestTemplate) {
    	this.restTemplate = restTemplate;
    	this.securityUserRestTemplate = adminRestTemplate;
    }

    public ResponseEntity putJson(String path, String json) {
        logRequest(path, "JSON", "PUT");
        return getRestTemplate().exchange(buildUri(path), HttpMethod.PUT, buildJsonEntity(json), String.class);
    }

	public ResponseEntity putJsonAsSecurityUser(String path, String json) {
		logSecurityUserRequest(path, "JSON", "PUT");
		return getSecurityUserRestTemplate().exchange(buildUri(path), HttpMethod.PUT, buildJsonEntity(json), String.class);
	}

    public ResponseEntity putXml(String path, String xml) {
        logRequest(path, "XML", "PUT");
        return getRestTemplate().exchange(buildUri(path), HttpMethod.PUT, buildXmlEntity(xml), String.class);
    }

	public ResponseEntity putXmlAsSecurityUser(String path, String xml) {
		logSecurityUserRequest(path, "XML", "PUT");
		return getSecurityUserRestTemplate().exchange(buildUri(path), HttpMethod.PUT, buildXmlEntity(xml), String.class);
	}

    public ResponseEntity postJson(String path, String json) {
        logRequest(path, "JSON", "POST");
        return getRestTemplate().exchange(buildUri(path), HttpMethod.POST, buildJsonEntity(json), String.class);
    }

	public ResponseEntity postJsonAsSecurityUser(String path, String json) {
		logSecurityUserRequest(path, "JSON", "POST");
		return getSecurityUserRestTemplate().exchange(buildUri(path), HttpMethod.POST, buildJsonEntity(json), String.class);
	}

    public ResponseEntity postXml(String path, String xml) {
        logRequest(path, "XML", "POST");
        return getRestTemplate().exchange(buildUri(path), HttpMethod.POST, buildXmlEntity(xml), String.class);
    }

	public ResponseEntity postXmlAsSecurityUser(String path, String xml) {
		logSecurityUserRequest(path, "XML", "POST");
		return getSecurityUserRestTemplate().exchange(buildUri(path), HttpMethod.POST, buildXmlEntity(xml), String.class);
	}

	public ResponseEntity postForm(String path, String... params) {
        logRequest(path, "form", "POST");
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        MultiValueMap map = new LinkedMultiValueMap<>();
        for (int i = 0; i < params.length; i += 2) {
            map.add(params[i], params[i + 1]);
        }
        HttpEntity> entity = new HttpEntity<>(map, headers);
        return getRestTemplate().exchange(buildUri(path), HttpMethod.POST, entity, String.class);
    }

    public String getXmlString(String path) {
        logRequest(path, "XML", "GET");
        return getRestTemplate().getForObject(buildUri(path), String.class);
    }

    public Fragment getXml(String path, String... namespacePrefixesAndUris) {
        String xml = getXmlString(path);
        List list = new ArrayList<>();
        for (int i = 0; i < namespacePrefixesAndUris.length; i += 2) {
            list.add(Namespace.getNamespace(namespacePrefixesAndUris[i], namespacePrefixesAndUris[i + 1]));
        }
        return new Fragment(xml, list.toArray(new Namespace[] {}));
    }

	public String getXmlStringAsSecurityUser(String path) {
		logSecurityUserRequest(path, "XML", "GET");
		return getSecurityUserRestTemplate().getForObject(buildUri(path), String.class);
	}

	public Fragment getXmlAsSecurityUser(String path, String... namespacePrefixesAndUris) {
		String xml = getXmlStringAsSecurityUser(path);
		List list = new ArrayList<>();
		for (int i = 0; i < namespacePrefixesAndUris.length; i += 2) {
			list.add(Namespace.getNamespace(namespacePrefixesAndUris[i], namespacePrefixesAndUris[i + 1]));
		}
		return new Fragment(xml, list.toArray(new Namespace[] {}));
	}

    public String getJson(String path) {
		return getJson(path, String.class).getBody();
    }

	public JsonNode getJsonNode(String path) {
		return getJson(path, JsonNode.class).getBody();
	}

	protected  ResponseEntity getJson(String path, Class responseType) {
		logRequest(path, "JSON", "GET");
		HttpHeaders headers = new HttpHeaders();
		headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
		return getRestTemplate().exchange(buildUri(path), HttpMethod.GET, new HttpEntity<>(headers), responseType);
	}

    public String getJson(URI uri) {
        logRequest(uri.toString(), "JSON", "GET");
        HttpHeaders headers = new HttpHeaders();
        headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
        return getRestTemplate().exchange(uri, HttpMethod.GET, new HttpEntity<>(headers), String.class).getBody();
    }

	public String getJsonAsSecurityUser(String path) {
		logSecurityUserRequest(path, "JSON", "GET");
		HttpHeaders headers = new HttpHeaders();
		headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
		return getSecurityUserRestTemplate().exchange(buildUri(path), HttpMethod.GET, new HttpEntity<>(headers), String.class)
			.getBody();
	}

	public void delete(String path) {
        logRequest(path, "", "DELETE");
        getRestTemplate().delete(buildUri(path));
    }

    public void deleteAsSecurityUser(String path) {
	    logSecurityUserRequest(path, "", "DELETE");
	    getSecurityUserRestTemplate().delete(buildUri(path));
    }

	/**
	 * Per #187 and version 3.1.0, when an HttpEntity is constructed with a JSON payload, this method will check to see
	 * if it should "clean" the JSON via the Jackson library, which is primarily intended for removing comments from
	 * JSON (comments that Jackson allows, but aren't allowed by the JSON spec). This behavior is disabled by default.
	 *
	 * @param json
	 * @return
	 */
	public HttpEntity buildJsonEntity(String json) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        if (manageConfig != null && manageConfig.isCleanJsonPayloads()) {
        	json = cleanJsonPayload(json);
        }
        return new HttpEntity<>(json, headers);
    }

	/**
	 * Per #187, and version 3.1.0, this will also use Jackson to remove any comments in the JSON payload, as Jackson
	 * is now configured to ignore comments, but we still don't want to include them in the payload sent to MarkLogic.
	 * @param payload
	 * @return
	 */
	protected String cleanJsonPayload(String payload) {
		if (payloadParser == null) {
			payloadParser = new PayloadParser();
		}
		JsonNode node = payloadParser.parseJson(payload);
		StringWriter sw = new StringWriter();
		try {
			ObjectMapperFactory.getObjectMapper().writer().writeValue(sw, node);
		} catch (IOException ex) {
			throw new RuntimeException("Unable to write JSON payload as JsonNode back out to a string, cause: " + ex.getMessage());
		}
		return sw.toString();
	}

	public HttpEntity buildXmlEntity(String xml) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_XML);
        return new HttpEntity<>(xml, headers);
    }

    protected void logRequest(String path, String contentType, String method) {
        if (logger.isInfoEnabled()) {
        	String username = manageConfig != null ?
				String.format("as user '%s' ", manageConfig.getUsername()) : "";
            logger.info("Sending {} {} request {}to path: {}", contentType, method, username, buildUri(path));
        }
    }

    protected void logSecurityUserRequest(String path, String contentType, String method) {
        if (logger.isInfoEnabled()) {
			String username = determineUsernameForSecurityUserRequest();
			if (!"".equals(username)) {
				username = String.format("as user '%s' (who should have the 'manage-admin' and 'security' roles) ", username);
			}
            logger.info("Sending {} {} request {}to path: {}", contentType, method, username, buildUri(path));
        }
    }

    protected String determineUsernameForSecurityUserRequest() {
	    String username = "";
	    if (manageConfig != null) {
		    username = manageConfig.getSecurityUsername();
		    if (!StringUtils.hasText(username)) {
			    username = manageConfig.getUsername();
		    }
	    }
	    return username;
    }

	private void initializeSecurityUserRestTemplate() {
		String securityUsername = this.manageConfig.getSecurityUsername();
		if (securityUsername != null && securityUsername.trim().length() > 0 && !securityUsername.equals(this.manageConfig.getUsername())) {
			if (logger.isInfoEnabled()) {
				logger.info(format("Initializing separate connection to Manage API with user '%s' that should have the 'manage-admin' and 'security' roles", securityUsername));
			}

			RestConfig rc = new RestConfig(this.manageConfig);
			// Override settings based on the 3 "security user"-specific properties known by ManageConfig.
			// Note that in 4.5.0, with the addition of cloud/certificate/kerberos/saml auth, this will only have any
			// impact if the user is using digest or basic auth. There's no equivalent of a separate "security" user
			// yet for the other 4 authentication types.
			rc.setUsername(this.manageConfig.getSecurityUsername());
			rc.setPassword(this.manageConfig.getSecurityPassword());
			if (this.manageConfig.getSecuritySslContext() != null) {
				rc.setSslContext(this.manageConfig.getSecuritySslContext());
			}
			this.securityUserRestTemplate = RestTemplateUtil.newRestTemplate(rc);
		} else {
			this.securityUserRestTemplate = getRestTemplate();
		}
	}

	public URI buildUri(String path) {
        return manageConfig.buildUri(path);
    }

    public RestTemplate getRestTemplate() {
        if (this.restTemplate == null) {
			this.restTemplate = RestTemplateUtil.newRestTemplate(this.manageConfig);
		}
		return restTemplate;
    }

    public ManageConfig getManageConfig() {
        return manageConfig;
    }

	public void setRestTemplate(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
	}

	public RestTemplate getSecurityUserRestTemplate() {
		if (this.securityUserRestTemplate == null) {
			initializeSecurityUserRestTemplate();
		}
		return securityUserRestTemplate;
	}

	public void setSecurityUserRestTemplate(RestTemplate restTemplate) {
		this.securityUserRestTemplate = restTemplate;
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy