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

com.kenticocloud.delivery.DeliveryClient Maven / Gradle / Ivy

There is a newer version: 1.0.6
Show newest version
/*
 * MIT License
 *
 * Copyright (c) 2017
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.kenticocloud.delivery;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.JSR310Module;
import com.kenticocloud.delivery.template.TemplateEngineConfig;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * Executes requests against the Kentico Cloud Delivery API.
 */
public class DeliveryClient {

    private static final Logger logger = LoggerFactory.getLogger(DeliveryClient.class);

    static final String ITEMS = "items";
    static final String TYPES = "types";
    static final String ELEMENTS = "elements";
    static final String TAXONOMIES = "taxonomies";

    private static final String URL_CONCAT = "%s/%s";

    private ObjectMapper objectMapper = new ObjectMapper();
    private PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
    private HttpClient httpClient;
    private DeliveryOptions deliveryOptions;

    private ContentLinkUrlResolver contentLinkUrlResolver;
    private BrokenLinkUrlResolver brokenLinkUrlResolver;
    private RichTextElementResolver richTextElementResolver = new DelegatingRichTextElementResolver();
    private StronglyTypedContentItemConverter stronglyTypedContentItemConverter =
            new StronglyTypedContentItemConverter();
    private TemplateEngineConfig templateEngineConfig;

    private CacheManager cacheManager = (requestUri, executor) -> executor.execute();

    /**
     * Initializes a new instance of the {@link DeliveryClient} class for retrieving content of the specified project.
     * @throws IllegalArgumentException Thrown if the arguments in the {@link DeliveryOptions} are invalid.
     * @param deliveryOptions The settings of the Kentico Cloud project.
     */
    public DeliveryClient(DeliveryOptions deliveryOptions) {
        this(deliveryOptions, new TemplateEngineConfig());
    }

    /**
     * Initializes a new instance of the {@link DeliveryClient} class for retrieving content of the specified project.
     * @throws IllegalArgumentException Thrown if the arguments in the {@link DeliveryOptions} are invalid.
     * @param deliveryOptions The settings of the Kentico Cloud project.
     * @param templateEngineConfig Configuration object used for customization of template render engines
     *                             for inline content
     */
    public DeliveryClient(DeliveryOptions deliveryOptions, TemplateEngineConfig templateEngineConfig) {
        if (deliveryOptions == null) {
            throw new IllegalArgumentException("The Delivery options object is not specified.");
        }
        if (deliveryOptions.getProjectId() == null || deliveryOptions.getProjectId().isEmpty()) {
            throw new IllegalArgumentException("Kentico Cloud project identifier is not specified.");
        }
        try {
            UUID.fromString(deliveryOptions.getProjectId());
        } catch (IllegalArgumentException e) {
            throw new IllegalArgumentException(
                    String.format(
                            "Provided string is not a valid project identifier (%s).  Have you accidentally passed " +
                                    "the Preview API key instead of the project identifier?",
                            deliveryOptions.getProjectId()),
                    e);
        }
        if (deliveryOptions.isUsePreviewApi() &&
                (deliveryOptions.getPreviewApiKey() == null || deliveryOptions.getPreviewApiKey().isEmpty())) {
            throw new IllegalArgumentException("The Preview API key is not specified.");
        }
        this.deliveryOptions = deliveryOptions;
        connManager.setMaxTotal(20);
        connManager.setDefaultMaxPerRoute(20);
        httpClient = HttpClients.custom().setConnectionManager(connManager).build();
        if (templateEngineConfig != null) {
            templateEngineConfig.init();
            this.templateEngineConfig = templateEngineConfig;
        }
        reconfigureDeserializer();
    }

    /**
     * Initializes a new instance of the {@link DeliveryClient} class for retrieving content of the specified project.
     * @throws IllegalArgumentException Thrown if the Project id is invalid.
     * @param projectId The Project ID associated with your Kentico Cloud account.  Must be in the format of an
     * {@link java.util.UUID}.
     */
    public DeliveryClient(String projectId) {
        this(new DeliveryOptions(projectId));
    }

    /**
     * Initializes a new instance of the {@link DeliveryClient} class for retrieving content of the specified project,
     * and configures the preview API.
     * 

* An API key (which comes in the form of a verified JSON Web Token) provides * read-only access to a single project. You can find the API keys for your project in the API keys section in the * Kentico Cloud app. * @throws IllegalArgumentException Thrown if the Project id is invalid. * @param projectId The Project ID associated with your Kentico Cloud account. Must be in the format of an * {@link java.util.UUID}. * @param previewApiKey The Preview API key configured with your Kentico Cloud account. */ public DeliveryClient(String projectId, String previewApiKey) { this(new DeliveryOptions(projectId, previewApiKey)); } public ContentItemsListingResponse getItems() throws IOException { return getItems(new ArrayList<>()); } public ContentItemsListingResponse getItems(List params) throws IOException { HttpUriRequest request = buildGetRequest(ITEMS, params); ContentItemsListingResponse contentItemsListingResponse = executeRequest(request, ContentItemsListingResponse.class); contentItemsListingResponse.setStronglyTypedContentItemConverter(stronglyTypedContentItemConverter); RichTextElementConverter converter = new RichTextElementConverter( getContentLinkUrlResolver(), getBrokenLinkUrlResolver(), getRichTextElementResolver(), templateEngineConfig, stronglyTypedContentItemConverter ); converter.process(contentItemsListingResponse.getItems()); return contentItemsListingResponse; } public List getItems(Class tClass, List params) throws IOException { ContentItemsListingResponse contentItemsListingResponse = getItems(params); return contentItemsListingResponse.castTo(tClass); } public ContentItemResponse getItem(String contentItemCodename) throws IOException { return getItem(contentItemCodename, new ArrayList<>()); } public List getItems(Class tClass) throws IOException { return getItems(tClass, new ArrayList<>()); } public Page getPageOfItems(Class tClass, List params) throws IOException { ContentItemsListingResponse response = getItems(params); return new Page<>(response, tClass, this); } public Page getNextPage(Page currentPage) throws IOException { Pagination pagination = currentPage.getPagination(); if (pagination.getNextPage() == null || pagination.getNextPage().isEmpty()) { return null; } RequestBuilder requestBuilder = RequestBuilder.get(pagination.getNextPage()); requestBuilder = addHeaders(requestBuilder); HttpUriRequest httpUriRequest = requestBuilder.build(); ContentItemsListingResponse response = executeRequest(httpUriRequest, ContentItemsListingResponse.class); RichTextElementConverter converter = new RichTextElementConverter( getContentLinkUrlResolver(), getBrokenLinkUrlResolver(), getRichTextElementResolver(), templateEngineConfig, stronglyTypedContentItemConverter ); converter.process(response.getItems()); return new Page<>(response, currentPage.getType(), this); } public T getItem(String contentItemCodename, Class tClass) throws IOException { return getItem(contentItemCodename, tClass, new ArrayList<>()); } public ContentItemResponse getItem(String contentItemCodename, List params) throws IOException { HttpUriRequest request = buildGetRequest(String.format(URL_CONCAT, ITEMS, contentItemCodename), params); ContentItemResponse contentItemResponse = executeRequest(request, ContentItemResponse.class); contentItemResponse.setStronglyTypedContentItemConverter(stronglyTypedContentItemConverter); RichTextElementConverter converter = new RichTextElementConverter( getContentLinkUrlResolver(), getBrokenLinkUrlResolver(), getRichTextElementResolver(), templateEngineConfig, stronglyTypedContentItemConverter ); converter.process(contentItemResponse.getItem()); return contentItemResponse; } public T getItem(String contentItemCodename, Class tClass, List params) throws IOException { ContentItemResponse contentItemResponse = getItem(contentItemCodename, params); return contentItemResponse.castTo(tClass); } public ContentTypesListingResponse getTypes() throws IOException { return getTypes(new ArrayList<>()); } public ContentTypesListingResponse getTypes(List params) throws IOException { HttpUriRequest request = buildGetRequest(TYPES, params); return executeRequest(request, ContentTypesListingResponse.class); } public ContentType getType(String contentTypeCodeName) throws IOException { return getType(contentTypeCodeName, new ArrayList<>()); } public ContentType getType(String contentTypeCodeName, List params) throws IOException { HttpUriRequest request = buildGetRequest(String.format(URL_CONCAT, TYPES, contentTypeCodeName), params); return executeRequest(request, ContentType.class); } public Element getContentTypeElement(String contentTypeCodeName, String elementCodeName) throws IOException { return getContentTypeElement(contentTypeCodeName, elementCodeName, new ArrayList<>()); } public Element getContentTypeElement(String contentTypeCodeName, String elementCodeName, List params) throws IOException { HttpUriRequest request = buildGetRequest( String.format("%s/%s/%s/%s", TYPES, contentTypeCodeName, ELEMENTS, elementCodeName), params); return executeRequest(request, Element.class); } public TaxonomyGroupListingResponse getTaxonomyGroups() throws IOException { return getTaxonomyGroups(new ArrayList<>()); } public TaxonomyGroupListingResponse getTaxonomyGroups(List params) throws IOException { HttpUriRequest request = buildGetRequest(TAXONOMIES, params); return executeRequest(request, TaxonomyGroupListingResponse.class); } public TaxonomyGroup getTaxonomyGroup(String taxonomyGroupCodename) throws IOException { return getTaxonomyGroup(taxonomyGroupCodename, new ArrayList<>()); } public TaxonomyGroup getTaxonomyGroup(String taxonomyGroupCodename, List params) throws IOException { HttpUriRequest request = buildGetRequest( String.format("%s/%s", TAXONOMIES, taxonomyGroupCodename), params); return executeRequest(request, TaxonomyGroup.class); } public ContentLinkUrlResolver getContentLinkUrlResolver() { return contentLinkUrlResolver; } public void setContentLinkUrlResolver(ContentLinkUrlResolver contentLinkUrlResolver) { this.contentLinkUrlResolver = contentLinkUrlResolver; } public BrokenLinkUrlResolver getBrokenLinkUrlResolver() { return brokenLinkUrlResolver; } public void setBrokenLinkUrlResolver(BrokenLinkUrlResolver brokenLinkUrlResolver) { this.brokenLinkUrlResolver = brokenLinkUrlResolver; } public RichTextElementResolver getRichTextElementResolver() { return richTextElementResolver; } public void setRichTextElementResolver(RichTextElementResolver richTextElementResolver) { this.richTextElementResolver = richTextElementResolver; } public void addRichTextElementResolver(RichTextElementResolver richTextElementResolver) { if (this.richTextElementResolver instanceof DelegatingRichTextElementResolver) { ((DelegatingRichTextElementResolver) this.richTextElementResolver).addResolver(richTextElementResolver); } else if (this.richTextElementResolver == null) { setRichTextElementResolver(richTextElementResolver); } else { DelegatingRichTextElementResolver delegatingResolver = new DelegatingRichTextElementResolver(); delegatingResolver.addResolver(this.richTextElementResolver); delegatingResolver.addResolver(richTextElementResolver); setRichTextElementResolver(delegatingResolver); } } public void registerType(String contentType, Class clazz) { stronglyTypedContentItemConverter.registerType(contentType, clazz); } public void registerType(Class clazz) { stronglyTypedContentItemConverter.registerType(clazz); } public void registerInlineContentItemsResolver(InlineContentItemsResolver resolver) { stronglyTypedContentItemConverter.registerInlineContentItemsResolver(resolver); } public void scanClasspathForMappings(String basePackage) { stronglyTypedContentItemConverter.scanClasspathForMappings(basePackage); } public void setCacheManager(CacheManager cacheManager) { this.cacheManager = cacheManager; } public void setMaxConnections(int maxConnections) { connManager.setDefaultMaxPerRoute(maxConnections); } protected HttpUriRequest buildGetRequest(String apiCall, List nameValuePairs) { RequestBuilder requestBuilder = RequestBuilder.get(String.format(URL_CONCAT, getBaseUrl(), apiCall)); requestBuilder = addHeaders(requestBuilder); for (NameValuePair nameValuePair : nameValuePairs) { requestBuilder.addParameter(nameValuePair); } return requestBuilder.build(); } protected RequestBuilder addHeaders(RequestBuilder requestBuilder) { if (deliveryOptions.isUsePreviewApi()) { requestBuilder.setHeader( HttpHeaders.AUTHORIZATION, String.format("Bearer %s", deliveryOptions.getPreviewApiKey()) ); } if (deliveryOptions.isWaitForLoadingNewContent()) { requestBuilder.setHeader( "X-KC-Wait-For-Loading-New-Content", "true" ); } requestBuilder.setHeader(HttpHeaders.ACCEPT, "application/json"); return requestBuilder; } private String getBaseUrl() { if (deliveryOptions.isUsePreviewApi()) { return String.format(deliveryOptions.getPreviewEndpoint(), deliveryOptions.getProjectId()); } else { return String.format(deliveryOptions.getProductionEndpoint(), deliveryOptions.getProjectId()); } } private T executeRequest(HttpUriRequest request, Class tClass) throws IOException { String requestUri = request.getURI().toString(); logger.info("HTTP {} - {} - {}", request.getMethod(), request.getAllHeaders(), requestUri); JsonNode jsonNode = cacheManager.resolveRequest(requestUri, () -> { HttpResponse response = httpClient.execute(request); handleErrorIfNecessary(response); InputStream inputStream = response.getEntity().getContent(); JsonNode node = objectMapper.readValue(inputStream, JsonNode.class); logger.info("{} - {}", response.getStatusLine(), requestUri); logger.debug("{} - {}:\n{}", request.getMethod(), requestUri, node); inputStream.close(); return node; }); return objectMapper.treeToValue(jsonNode, tClass); } private void handleErrorIfNecessary(HttpResponse response) throws IOException { final int status = response.getStatusLine().getStatusCode(); if (status >= 500) { logger.error("Kentico API server error, status: {}", status); throw new IOException("Unknown error with Kentico API. Kentico is likely suffering site issues."); } else if (status >= 400) { logger.error("Kentico API request error, status: ", status); InputStream inputStream = response.getEntity().getContent(); KenticoError kenticoError = objectMapper.readValue(inputStream, KenticoError.class); inputStream.close(); throw new KenticoErrorException(kenticoError); } } private void reconfigureDeserializer() { objectMapper = new ObjectMapper(); SimpleModule module = new SimpleModule(); objectMapper.registerModule(new JSR310Module()); objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.registerModule(module); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy