org.ambraproject.wombat.service.remote.AbstractContentApi.java Source code

Java tutorial

Introduction

Here is the source code for org.ambraproject.wombat.service.remote.AbstractContentApi.java

Source

/*
 * Copyright (c) 2017 Public Library of Science
 *
 * 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 org.ambraproject.wombat.service.remote;

import com.google.gson.Gson;
import org.ambraproject.wombat.util.CacheKey;
import org.ambraproject.wombat.util.UrlParamBuilder;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;

public abstract class AbstractContentApi implements ContentApi {

    @Autowired
    private ArticleApi articleApi;
    @Autowired
    private CachedRemoteService<InputStream> cachedRemoteStreamer;
    @Autowired
    private CachedRemoteService<Reader> cachedRemoteReader;
    @Autowired
    protected Gson gson;

    private static class RepoConfig {
        private final String address;
        private final String bucketName;

        private RepoConfig(URI address, String bucketName) {
            this.address = sanitizeAddress(address);
            this.bucketName = Objects.requireNonNull(bucketName);
        }

        private static String sanitizeAddress(URI address) {
            String addressString = address.toString();
            return addressString.endsWith("/") ? addressString.substring(0, addressString.length() - 1)
                    : addressString;
        }
    }

    private static final ApiAddress REPO_CONFIG_ADDRESS = ApiAddress.builder("config").addParameter("type", "repo")
            .build();

    private transient RepoConfig repoConfig;

    private RepoConfig getRepoConfig() throws IOException {
        if (this.repoConfig != null)
            return repoConfig;
        final String repoConfigKey = getRepoConfigKey();
        Map<String, Object> serverRepoInfo = (Map<String, Object>) articleApi.requestObject(REPO_CONFIG_ADDRESS,
                Map.class);
        Map<String, Object> repoInfo = (Map<String, Object>) serverRepoInfo.get(repoConfigKey);
        if (repoInfo == null) {
            throw new RuntimeException(
                    String.format("config?type=repo did not provide \"%s\" config", repoConfigKey));
        }
        String address = (String) repoInfo.get("address");
        if (address == null) {
            throw new RuntimeException(
                    String.format("config?type=repo did not provide \"%s.address\"", repoConfigKey));
        }
        String bucket = (String) repoInfo.get("bucket");
        if (bucket == null) {
            throw new RuntimeException(
                    String.format("config?type=repo did not provide \"%s.bucket\"", repoConfigKey));
        }

        URI contentRepoAddress;
        try {
            contentRepoAddress = new URI(address);
        } catch (URISyntaxException e) {
            throw new RuntimeException("Invalid content repo URI returned from service", e);
        }
        return this.repoConfig = new RepoConfig(contentRepoAddress, bucket);
    }

    protected abstract String getRepoConfigKey();

    @Override
    public final CloseableHttpResponse request(ContentKey key, Collection<? extends Header> headers)
            throws IOException {
        URI requestAddress = buildUri(key, RequestMode.OBJECT);
        HttpGet get = new HttpGet(requestAddress);
        get.setHeaders(headers.toArray(new Header[headers.size()]));
        return cachedRemoteStreamer.getResponse(get);
    }

    /**
     * Designates whether a request to the Content Repo service is for an object (file content) or metadata (JSON).
     */
    private static enum RequestMode {
        OBJECT, METADATA;

        private String getPathComponent() {
            switch (this) {
            case OBJECT:
                return "objects";
            case METADATA:
                return "objects/meta";
            default:
                throw new AssertionError();
            }
        }
    }

    private URI buildUri(ContentKey key, RequestMode mode) throws IOException {
        RepoConfig repoConfig = getRepoConfig();
        UrlParamBuilder requestParams = UrlParamBuilder.params();
        key.setParameters(requestParams);
        String repoBucketName = key.getBucketName() != null ? key.getBucketName() : repoConfig.bucketName;
        return URI.create(String.format("%s/%s/%s?%s", repoConfig.address, mode.getPathComponent(), repoBucketName,
                requestParams.format()));
    }

    protected final <T> T requestCachedReader(CacheKey cacheKey, ContentKey key,
            CacheDeserializer<Reader, T> callback) throws IOException {
        HttpGet target = new HttpGet(buildUri(key, RequestMode.OBJECT));
        return cachedRemoteReader.requestCached(cacheKey, target, callback);
    }

    protected final <T> T requestCachedStream(CacheKey cacheKey, ContentKey key,
            CacheDeserializer<InputStream, T> callback) throws IOException {
        HttpGet target = new HttpGet(buildUri(key, RequestMode.OBJECT));
        return cachedRemoteStreamer.requestCached(cacheKey, target, callback);
    }

    @Override
    public final Map<String, Object> requestMetadata(CacheKey cacheKey, ContentKey key) throws IOException {
        return cachedRemoteReader.requestCached(cacheKey, new HttpGet(buildUri(key, RequestMode.METADATA)),
                (Reader stream) -> gson.fromJson(stream, Map.class));
    }

}