org.codice.ddf.catalog.async.data.impl.LazyProcessResourceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.codice.ddf.catalog.async.data.impl.LazyProcessResourceImpl.java

Source

/**
 * Copyright (c) Codice Foundation
 *
 * <p>This is free software: you can redistribute it and/or modify it under the terms of the GNU
 * Lesser General Public License as published by the Free Software Foundation, either version 3 of
 * the License, or any later version.
 *
 * <p>This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details. A copy of the GNU Lesser General Public
 * License is distributed along with this program and can be found at
 * <http://www.gnu.org/licenses/lgpl.html>.
 */
package org.codice.ddf.catalog.async.data.impl;

import static org.codice.ddf.catalog.async.data.impl.ProcessResourceImpl.DEFAULT_MIME_TYPE;
import static org.codice.ddf.catalog.async.data.impl.ProcessResourceImpl.DEFAULT_NAME;

import com.google.common.io.Closer;
import ddf.catalog.resource.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.function.Supplier;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.codice.ddf.catalog.async.data.api.internal.InaccessibleResourceException;
import org.codice.ddf.catalog.async.data.api.internal.ProcessResource;
import org.codice.ddf.platform.util.TemporaryFileBackedOutputStream;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class LazyProcessResourceImpl implements ProcessResource {

    private static final Logger LOGGER = LoggerFactory.getLogger(LazyProcessResourceImpl.class);

    private static final int FILE_BACKED_OUTPUT_STREAM_THRESHOLD = 32 * 1024; // 32 kilobytes

    private URI uri;

    private String name = DEFAULT_NAME;

    private String mimeType = DEFAULT_MIME_TYPE;

    private volatile long size = UNKNOWN_SIZE;

    private InputStream inputStream;

    private TemporaryFileBackedOutputStream resourceDataCache;

    private Closer streamCloser;

    private String qualifier;

    private boolean isModified = false;

    private boolean isResourceLoaded = false;

    private Supplier<Resource> resourceSupplier;

    private String metacardId;

    /**
     * Creates a {@link ProcessResource} with a {@link Supplier} used to load the {@link Resource}.
     * The resource will not be loaded until the first time one of the following fields is accessed:
     * inputStream mimeType name
     *
     * @param metacardId schema specific part of {@link URI}, throws {@link IllegalArgumentException}
     *     if empty or null
     * @param resourceSupplier a {@link Supplier} used to load the resource
     * @throws IllegalArgumentException if the input provided is not valid
     */
    public LazyProcessResourceImpl(String metacardId, Supplier<Resource> resourceSupplier) {
        if (StringUtils.isBlank(metacardId)) {
            throw new IllegalArgumentException(
                    "ProcessResourceImpl argument \"metacardId\" may not be null or empty.");
        }
        if (resourceSupplier == null) {
            throw new IllegalArgumentException("LazyProcessResourceImpl must have a non null resource supplier");
        }

        this.isModified = false;
        this.metacardId = metacardId;
        this.resourceSupplier = resourceSupplier;
        this.isResourceLoaded = false;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This will load the resource.
     */
    @Override
    public synchronized InputStream getInputStream() throws IOException {
        if (resourceDataCache == null) {
            loadResource();

            if (inputStream == null) {
                throw new IOException(String.format("Tried to get input stream for %s but was null", name));
            }
            resourceDataCache = new TemporaryFileBackedOutputStream(FILE_BACKED_OUTPUT_STREAM_THRESHOLD);
            IOUtils.copyLarge(inputStream, resourceDataCache);
            IOUtils.closeQuietly(inputStream);
            streamCloser.register(resourceDataCache);
        }
        InputStream newInputStream = resourceDataCache.asByteSource().openStream();
        streamCloser.register(newInputStream);

        return newInputStream;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This will load the resource.
     */
    @Override
    public synchronized String getMimeType() {
        loadResource();
        return mimeType;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This will load the resource.
     */
    @Override
    public synchronized String getName() {
        loadResource();
        return name;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This value should not require the resources to be loaded.
     */
    @Override
    public String getQualifier() {
        return qualifier;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This value should not require the resources to be loaded.
     */
    @Override
    public long getSize() {
        return size;
    }

    /**
     * {@inheritDoc}
     *
     * <p>This value should not require the resources to be loaded.
     */
    @Override
    public URI getUri() {
        return uri;
    }

    public void setSize(long size) {
        this.size = size;
    }

    public void setUri(URI uri) {
        this.uri = uri;

        if (uri != null && StringUtils.isNotBlank(uri.getFragment())) {
            this.qualifier = uri.getFragment();
        }
    }

    @Override
    public boolean isModified() {
        return isModified;
    }

    @Override
    public synchronized void close() {
        IOUtils.closeQuietly(streamCloser);
    }

    public void markAsModified() {
        isModified = true;
    }

    private synchronized void loadResource() {
        if (!isResourceLoaded) {
            populateProcessResource(resourceSupplier.get());
        }
    }

    private synchronized void populateProcessResource(Resource resource) {
        if (resource == null) {
            String message = "Error loading resource for metacard id: " + metacardId + ", URI: " + uri
                    + ". Null resource was returned by supplier. The resource hasn't been loaded.";
            LOGGER.debug(message);

            throw new InaccessibleResourceException(message);
        }

        this.inputStream = resource.getInputStream();
        this.streamCloser = Closer.create();
        this.streamCloser.register(inputStream);

        if (this.size == UNKNOWN_SIZE) {
            this.size = resource.getSize();
        }

        String resourceName = resource.getName();
        if (StringUtils.isNotBlank(resourceName)) {
            this.name = resourceName;
        }

        String resourceMimeType = resource.getMimeTypeValue();
        if (StringUtils.isNotBlank(resourceMimeType)) {
            this.mimeType = resourceMimeType;
        }

        isResourceLoaded = true;
    }
}