org.commonjava.indy.core.ctl.ContentController.java Source code

Java tutorial

Introduction

Here is the source code for org.commonjava.indy.core.ctl.ContentController.java

Source

/**
 * Copyright (C) 2011-2018 Red Hat, Inc. (https://github.com/Commonjava/indy)
 *
 * 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 org.commonjava.indy.core.ctl;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.commonjava.indy.IndyWorkflowException;
import org.commonjava.indy.content.ContentManager;
import org.commonjava.indy.content.StoreResource;
import org.commonjava.indy.data.IndyDataException;
import org.commonjava.indy.data.StoreDataManager;
import org.commonjava.indy.model.core.ArtifactStore;
import org.commonjava.indy.model.core.StoreKey;
import org.commonjava.indy.model.core.StoreType;
import org.commonjava.indy.model.core.dto.DirectoryListingDTO;
import org.commonjava.indy.model.galley.KeyedLocation;
import org.commonjava.indy.subsys.template.IndyGroovyException;
import org.commonjava.indy.subsys.template.TemplatingEngine;
import org.commonjava.indy.util.ApplicationContent;
import org.commonjava.indy.util.ApplicationStatus;
import org.commonjava.indy.util.MimeTyper;
import org.commonjava.indy.util.UriFormatter;
import org.commonjava.maven.galley.event.EventMetadata;
import org.commonjava.maven.galley.model.ConcreteResource;
import org.commonjava.maven.galley.model.Transfer;
import org.commonjava.maven.galley.model.TransferOperation;
import org.commonjava.maven.galley.transport.htcli.model.HttpExchangeMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
import javax.ws.rs.core.MediaType;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;

import static org.apache.commons.io.IOUtils.closeQuietly;
import static org.commonjava.maven.galley.util.PathUtils.normalize;
import static org.commonjava.maven.galley.util.PathUtils.parentPath;

@ApplicationScoped
public class ContentController {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    public static final String LISTING_HTML_FILE = "index.html";

    private static final int MAX_PEEK_COUNT = 100;

    public static final String HTML_TAG_PATTERN = ".*\\<(!DOCTYPE|[-_.a-zA-Z0-9]+).*";

    private static final int MAX_PEEK_BYTES = 16384;

    @Inject
    private StoreDataManager storeManager;

    @Inject
    private ContentManager contentManager;

    @Inject
    private TemplatingEngine templates;

    @Inject
    private ObjectMapper mapper;

    @Inject
    private MimeTyper mimeTyper;

    protected ContentController() {
    }

    public ContentController(final StoreDataManager storeManager, final ContentManager contentManager,
            final TemplatingEngine templates, final ObjectMapper mapper, final MimeTyper mimeTyper) {
        this.storeManager = storeManager;
        this.contentManager = contentManager;
        this.templates = templates;
        this.mapper = mapper;
        this.mimeTyper = mimeTyper;
    }

    public ApplicationStatus delete(final StoreType type, final String name, final String path)
            throws IndyWorkflowException {
        return delete(type, name, path, new EventMetadata());
    }

    public ApplicationStatus delete(final StoreType type, final String name, final String path,
            final EventMetadata eventMetadata) throws IndyWorkflowException {
        return delete(new StoreKey(type, name), path, eventMetadata);
    }

    public ApplicationStatus delete(final StoreKey key, final String path) throws IndyWorkflowException {
        return delete(key, path, new EventMetadata());
    }

    public ApplicationStatus delete(final StoreKey key, final String path, final EventMetadata eventMetadata)
            throws IndyWorkflowException {
        final ArtifactStore store = getStore(key);

        final boolean deleted = contentManager.delete(store, path, eventMetadata);
        return deleted ? ApplicationStatus.NO_CONTENT : ApplicationStatus.NOT_FOUND;
    }

    public Transfer get(final StoreKey key, final String path) throws IndyWorkflowException {
        return get(key, path, new EventMetadata());
    }

    public Transfer get(final StoreKey key, final String path, final EventMetadata eventMetadata)
            throws IndyWorkflowException {
        final ArtifactStore store = getStore(key);

        return contentManager.retrieve(store, path, eventMetadata);
    }

    public String getContentType(final String path) {
        return mimeTyper.getContentType(path);
    }

    public Transfer store(final StoreType type, final String name, final String path, final InputStream stream)
            throws IndyWorkflowException {
        return store(type, name, path, stream, new EventMetadata());
    }

    public Transfer store(final StoreType type, final String name, final String path, final InputStream stream,
            final EventMetadata eventMetadata) throws IndyWorkflowException {
        return store(new StoreKey(type, name), path, stream, eventMetadata);
    }

    public Transfer store(final StoreKey key, final String path, final InputStream stream)
            throws IndyWorkflowException {
        return store(key, path, stream, new EventMetadata());
    }

    public Transfer store(final StoreKey key, final String path, final InputStream stream,
            final EventMetadata eventMetadata) throws IndyWorkflowException {
        final ArtifactStore store = getStore(key);

        logger.info("Storing: {} in: {} with event metadata: {}", path, store, eventMetadata);

        return contentManager.store(store, path, stream, TransferOperation.UPLOAD, eventMetadata);
    }

    public void rescan(final StoreKey key) throws IndyWorkflowException {
        rescan(key, new EventMetadata());
    }

    public void rescan(final StoreKey key, final EventMetadata eventMetadata) throws IndyWorkflowException {
        final ArtifactStore artifactStore = getStore(key);
        contentManager.rescan(artifactStore, eventMetadata);
    }

    public void rescanAll() throws IndyWorkflowException {
        rescanAll(new EventMetadata());
    }

    public void rescanAll(final EventMetadata eventMetadata) throws IndyWorkflowException {
        try {
            final List<ArtifactStore> stores = storeManager.query().concreteStores().getAll();
            contentManager.rescanAll(stores, eventMetadata);
        } catch (final IndyDataException e) {
            throw new IndyWorkflowException(ApplicationStatus.SERVER_ERROR.code(),
                    "Failed to retrieve list of concrete stores. Reason: {}", e, e.getMessage());
        }
    }

    public void deleteAll(final String path) throws IndyWorkflowException {
        deleteAll(path, new EventMetadata());
    }

    public void deleteAll(final String path, final EventMetadata eventMetadata) throws IndyWorkflowException {
        try {
            final List<ArtifactStore> stores = storeManager.query().concreteStores().getAll();
            contentManager.deleteAll(stores, path, eventMetadata);
        } catch (final IndyDataException e) {
            throw new IndyWorkflowException(ApplicationStatus.SERVER_ERROR.code(),
                    "Failed to retrieve list of concrete stores. Reason: {}", e, e.getMessage());
        }
    }

    public ArtifactStore getStore(final StoreKey key) throws IndyWorkflowException {
        ArtifactStore store;
        try {
            store = storeManager.getArtifactStore(key);
        } catch (final IndyDataException e) {
            throw new IndyWorkflowException(ApplicationStatus.SERVER_ERROR.code(),
                    "Cannot retrieve store: {}. Reason: {}", e, key, e.getMessage());
        }

        if (store == null) {
            throw new IndyWorkflowException(ApplicationStatus.NOT_FOUND.code(), "Cannot find store: {}", key);
        }

        if (store.isDisabled()) {
            throw new IndyWorkflowException(ApplicationStatus.NOT_FOUND.code(), "Store is disabled: {}", key);
        }

        return store;
    }

    public String renderListing(final String acceptHeader, final StoreType type, final String name,
            final String path, final String serviceUrl, final UriFormatter uriFormatter)
            throws IndyWorkflowException {
        final StoreKey key = new StoreKey(type, name);
        return renderListing(acceptHeader, key, path, serviceUrl, uriFormatter);
    }

    public String renderListing(final String acceptHeader, final StoreKey key, final String requestPath,
            final String serviceUrl, final UriFormatter uriFormatter) throws IndyWorkflowException {
        return renderListing(acceptHeader, key, requestPath, serviceUrl, uriFormatter, new EventMetadata());
    }

    public String renderListing(final String acceptHeader, final StoreKey key, final String requestPath,
            final String serviceUrl, final UriFormatter uriFormatter, final EventMetadata eventMetadata)
            throws IndyWorkflowException {
        String path = requestPath;
        if (path.endsWith(LISTING_HTML_FILE)) {
            path = normalize(parentPath(path));
        }

        final List<StoreResource> listed = getListing(key, path, eventMetadata);
        if (ApplicationContent.application_json.equals(acceptHeader)) {
            final DirectoryListingDTO dto = new DirectoryListingDTO(StoreResource.convertToEntries(listed));
            try {
                return mapper.writeValueAsString(dto);
            } catch (final JsonProcessingException e) {
                throw new IndyWorkflowException("Failed to render listing to JSON: %s. Reason: %s", e, dto,
                        e.getMessage());
            }
        }

        final Map<String, Set<String>> listingUrls = new TreeMap<>();

        final String storeUrl = uriFormatter.formatAbsolutePathTo(serviceUrl, key.getType().singularEndpointName(),
                key.getName());

        if (listed != null) {
            // first pass, process only obvious directory entries (ending in '/')
            // second pass, process the remainder.
            for (int pass = 0; pass < 2; pass++) {
                for (final ConcreteResource res : listed) {
                    String p = res.getPath();
                    if (pass == 0 && !p.endsWith("/")) {
                        continue;
                    }
                    if (p.endsWith("-") || p.endsWith("-/")) {
                        //skip npm adduser path to avoid the sensitive info showing.
                        continue;
                    } else if (pass == 1) {
                        if (!p.endsWith("/")) {
                            final String dirpath = p + "/";
                            if (listingUrls.containsKey(normalize(storeUrl, dirpath))) {
                                p = dirpath;
                            }
                        } else {
                            continue;
                        }
                    }

                    final String localUrl = normalize(storeUrl, p);
                    Set<String> sources = listingUrls.get(localUrl);
                    if (sources == null) {
                        sources = new HashSet<>();
                        listingUrls.put(localUrl, sources);
                    }

                    sources.add(normalize(res.getLocationUri(), res.getPath()));
                }
            }
        }

        final List<String> sources = new ArrayList<>();
        if (listed != null) {
            for (final ConcreteResource res : listed) {
                // KeyedLocation is all we use in Indy.
                logger.debug("Formatting sources URL for: {}", res);
                final KeyedLocation kl = (KeyedLocation) res.getLocation();

                final String uri = uriFormatter.formatAbsolutePathTo(serviceUrl,
                        kl.getKey().getType().singularEndpointName(), kl.getKey().getName());
                if (!sources.contains(uri)) {
                    logger.debug("adding source URI: '{}'", uri);
                    sources.add(uri);
                }
            }
        }

        Collections.sort(sources);

        String parentPath = normalize(parentPath(path));
        if (!parentPath.endsWith("/")) {
            parentPath += "/";
        }

        final String parentUrl;
        if (parentPath.equals(path)) {
            parentPath = null;
            parentUrl = null;
        } else {
            parentUrl = uriFormatter.formatAbsolutePathTo(serviceUrl, key.getType().singularEndpointName(),
                    key.getName(), parentPath);
        }

        final Map<String, Object> params = new HashMap<>();
        params.put("items", listingUrls);
        params.put("parentUrl", parentUrl);
        params.put("parentPath", parentPath);
        params.put("path", path);
        params.put("storeKey", key);
        params.put("storeUrl", storeUrl);
        params.put("baseUrl", serviceUrl);
        params.put("sources", sources);

        // render...
        try {
            return templates.render(acceptHeader, "directory-listing", params);
        } catch (final IndyGroovyException e) {
            throw new IndyWorkflowException(e.getMessage(), e);
        }
    }

    public List<StoreResource> getListing(final StoreType type, final String name, final String path)
            throws IndyWorkflowException {
        return getListing(new StoreKey(type, name), path);
    }

    public List<StoreResource> getListing(final StoreKey key, final String path) throws IndyWorkflowException {
        return getListing(key, path, new EventMetadata());
    }

    public List<StoreResource> getListing(final StoreKey key, final String path, final EventMetadata eventMetadata)
            throws IndyWorkflowException {
        final ArtifactStore store = getStore(key);
        return contentManager.list(store, path, eventMetadata);
    }

    public boolean isHtmlContent(final Transfer item) throws IndyWorkflowException {
        final byte[] head = new byte[MAX_PEEK_BYTES];
        BufferedReader reader = null;
        InputStream raw = null;
        try {
            raw = item.openInputStream(false);
            final int read = raw.read(head);
            if (read > 0) {
                reader = new BufferedReader(new InputStreamReader(new ByteArrayInputStream(head)));

                String line;
                int count = 0;
                while ((line = reader.readLine()) != null && count < MAX_PEEK_COUNT) {
                    if (line.matches(HTML_TAG_PATTERN)) {
                        return true;
                    }
                    count++;
                }
            }
        } catch (final IOException e) {
            throw new IndyWorkflowException("Cannot read: %s. Reason: %s", e, item, e.getMessage());
        } finally {
            closeQuietly(raw);
            closeQuietly(reader);
        }

        return false;
    }

    public Transfer getTransfer(final StoreKey storeKey, final String path, final TransferOperation op)
            throws IndyWorkflowException {
        return contentManager.getTransfer(storeKey, path, op);
    }

    public HttpExchangeMetadata getHttpMetadata(final StoreKey storeKey, final String path)
            throws IndyWorkflowException {
        logger.debug("Getting HTTP metadata for: {}/{}", storeKey, path);
        return contentManager.getHttpMetadata(storeKey, path);
    }

    public HttpExchangeMetadata getHttpMetadata(final Transfer txfr) throws IndyWorkflowException {
        return contentManager.getHttpMetadata(txfr);
    }

    public boolean exists(StoreKey sk, String path) throws IndyWorkflowException {
        return contentManager.exists(getStore(sk), path);
    }
}