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

com.redhat.ceylon.cmr.impl.RootRepositoryManager Maven / Gradle / Ivy

There is a newer version: 1.3.3
Show newest version
/*
 * Copyright 2011 Red Hat inc. and third party contributors as noted 
 * by the author tags.
 * 
 * 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.redhat.ceylon.cmr.impl;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;

import com.redhat.ceylon.cmr.api.ArtifactCallback;
import com.redhat.ceylon.cmr.api.ArtifactCallbackStream;
import com.redhat.ceylon.cmr.api.ArtifactContext;
import com.redhat.ceylon.cmr.api.CmrRepository;
import com.redhat.ceylon.cmr.api.Overrides;
import com.redhat.ceylon.cmr.spi.ContentStore;
import com.redhat.ceylon.cmr.spi.Node;
import com.redhat.ceylon.cmr.spi.OpenNode;
import com.redhat.ceylon.cmr.spi.SizedInputStream;
import com.redhat.ceylon.common.FileUtil;
import com.redhat.ceylon.common.config.Repositories;
import com.redhat.ceylon.common.log.Logger;
import com.redhat.ceylon.model.cmr.ArtifactResult;
import com.redhat.ceylon.model.cmr.RepositoryException;

/**
 * Root node -- main entry point into Ceylon repositories.
 *
 * @author Ales Justin
 */
public class RootRepositoryManager extends AbstractNodeRepositoryManager {
    private final FileContentStore fileContentStore;

    private static File getRootDir() {
        com.redhat.ceylon.common.config.Repositories.Repository rootRepo = Repositories.get().getCacheRepository();
        return new File(rootRepo.getUrl());
    }

    public RootRepositoryManager(Logger log, Overrides overrides) {
        this(getRootDir(), log, overrides);
    }
    
    public RootRepositoryManager(Logger log, Overrides overrides, boolean upgradeDist) {
        this(getRootDir(), log, overrides, upgradeDist);
    }

    public RootRepositoryManager(File rootDir, Logger log, Overrides overrides) {
        this(rootDir, log, overrides, true);
    }
    public RootRepositoryManager(File rootDir, Logger log, Overrides overrides, boolean upgradeDist) {
        super(log, overrides, upgradeDist);
        if(rootDir != null){
            this.fileContentStore = new FileContentStore(rootDir);
            final CmrRepository aaca = new DefaultRepository(new RootNode(fileContentStore, fileContentStore));
            setCache(aaca);
        }else{
            this.fileContentStore = null;
        }
    }

    

    @Override
    protected ArtifactResult getArtifactResult(ArtifactContext context, Node node) throws RepositoryException {
        if (node.isRemote()) {
            final boolean forceOp = context.isForceOperation();
            try {
                context.setForceOperation(true); // just force the ops
                log.debug("Looking up artifact " + context + " from " + node + " to cache it");
                SizedInputStream sizedInputStream = node.getSizedInputStream();
                // temp fix for https://github.com/ceylon/ceylon-module-resolver/issues/60
                // in theory we should not have nodes with null streams, but at least provide a helpful exception
                if (sizedInputStream == null) {
                    throw new RepositoryException("Node " + node + " for repository " + this + " returned a null stream");
                }
                try {
                    log.debug(" -> Found it, now caching it");
                    final File file = putContent(context, node, sizedInputStream.inputStream, sizedInputStream.size);
                    log.debug("    Caching done: " + file);
                    String repositoryDisplayString = NodeUtils.getRepositoryDisplayString(node);
                    File originalRepoFile = new File(file.getParentFile(), file.getName().concat(ORIGIN));                        
                    FileWriter writer = new FileWriter(originalRepoFile, false);
                    try {
                        writer.write(repositoryDisplayString);
                        writer.close();
                    } catch(IOException e) {
                        log.error(e.toString());
                    }
                    // we expect the remote nodes to support Ceylon module info
                    return new FileArtifactResult(NodeUtils.getRepository(node), this, context.getName(), context.getVersion(), file, repositoryDisplayString);
                } finally {
                    IOUtils.safeClose(sizedInputStream.inputStream);
                }
            } catch (IOException e) {
                throw new RepositoryException(e);
            } finally {
                context.setForceOperation(forceOp);
            }
        } else {
            return toArtifactResult(node);
        }
    }

    @Override
    protected ArtifactResult artifactNotFound(ArtifactContext context) throws RepositoryException {
        boolean hasRemote = false;
        StringBuilder reps = new StringBuilder();
        for (CmrRepository rep : getRepositories()) {
            if (rep.getRoot().isRemote() && !isOffline(rep)) {
                hasRemote = true;
                reps.append(rep.getDisplayString());
                reps.append('\n');
            }
        }

        if (hasRemote && cache != null) {
            // Create a .missing file in the cache to mark that we tried to locate the file but it didn't exist 
            Node parent = cache.findParent(context);
            if (parent != null) {
                context.toNode(parent);
                try {
                    // fileContentStore cannot be null if we have a cache
                    File parentDir = fileContentStore.getFile(parent);
                    String[] names = cache.getArtifactNames(context);
                    File missingFile = new File(parentDir, names[0].concat(MISSING));
                    if (!missingFile.exists()) {
                        if (context.getSearchRepository() == cache) {
                            ArtifactContext unpreferred = new ArtifactContext(context.getName(), context.getVersion(), context.getSuffixes());
                            unpreferred.copySettingsFrom(context);
                            return getArtifactResult(unpreferred);
                        } else {
                            FileUtil.mkdirs(parentDir);
                            try (FileWriter writer = new FileWriter(missingFile, false)) {
                                // We write the list of remote repositories we tried
                                // This is not currently used but might be useful in the future
                                writer.write(reps.toString());
                            } catch(IOException e) {
                                log.error(e.toString());
                            }
                        }
                    }
                } finally {
                    ArtifactContext.removeNode(parent);
                }
            }
        }
        
        return super.artifactNotFound(context);
    }
    
    private boolean isOffline(CmrRepository repo) {
        ContentStore cs = repo.getRoot().getService(ContentStore.class);
        return cs != null && cs.isOffline();
    }

    private File putContent(ArtifactContext context, Node node, InputStream stream, long length) throws IOException {
        log.debug("Creating local copy of external node: " + node + " at repo: " + 
                (fileContentStore != null ? fileContentStore.getDisplayString() : null));
        if(fileContentStore == null)
            throw new IOException("No location to place node at: fileContentStore is null");
        
        ArtifactCallback callback = context.getCallback();
        if (callback == null) {
            callback = ArtifactCallbackStream.getCallback();
        }
        final File file;
        try {
            if (callback != null) {
                callback.start(NodeUtils.getFullPath(node), length != -1 ? length : node.getSize(), node.getStoreDisplayString());
                stream = new ArtifactCallbackStream(callback, stream);
            }
            fileContentStore.putContent(node, stream, context); // stream should be closed closer to API call
            file = fileContentStore.getFile(node); // re-get
            if (callback != null) {
                callback.done(file);
            }
        } catch (Throwable t) {
            if (callback != null) {
                callback.error(fileContentStore.getFile(node), t);
            }
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw IOUtils.toIOException(t);
            }
        }

        if (context.isIgnoreSHA() == false && node instanceof OpenNode) {
            final OpenNode on = (OpenNode) node;
            final String sha1 = IOUtils.sha1(new FileInputStream(file));
            if (sha1 != null) {
                ByteArrayInputStream shaStream = new ByteArrayInputStream(sha1.getBytes("ASCII"));
                Node parent = NodeUtils.firstParent(node);
                if (parent == null) {
                    throw new IllegalArgumentException("Parent should not be null: " + node);
                }
                Node sha = parent.getChild(on.getLabel() + SHA1);
                if (sha == null) {
                    // put it to ext node as well, if supported
                    on.addContent(SHA1, shaStream, context);
                    shaStream.reset(); // reset, for next read
                } else if (sha.hasBinaries()) {
                    final String existingSha1 = IOUtils.readSha1(sha.getInputStream());
                    if (sha1.equals(existingSha1) == false) {
                        try {
                            fileContentStore.delete(file, node);
                        } catch (Exception e) {
                            log.warning("Error removing new content: " + file);
                        }
                        throw new IOException("Bad SHA1 - file: " + sha1 + " != " + existingSha1);
                    }
                }
                // create empty marker node
                OpenNode sl = ((OpenNode) parent).addNode(on.getLabel() + SHA1 + LOCAL);
                // put sha to local store as well
                fileContentStore.putContent(sl, shaStream, context);
            }
        }

        // only check for jars or forced checks
        if (ArtifactContext.JAR.equals(context.getSingleSuffix()) || context.forceDescriptorCheck()) {
            // transfer descriptor as well, if there is one
            final Node descriptor = Configuration.getResolvers(this).descriptor(node);
            if (descriptor != null && descriptor.hasBinaries()) {
                try (InputStream is = descriptor.getInputStream()) {
                    fileContentStore.putContent(descriptor, is, context);
                }
            }
        }

        // refresh markers from root to this newly put node
        if(getCache() != null){
            final List paths = NodeUtils.toLabelPath(node);
            OpenNode current = getCache();
            for (String path : paths) {
                if (current == null)
                    break;

                current.refresh(false);
                final Node tmp = current.peekChild(path);
                current = (tmp instanceof OpenNode) ? OpenNode.class.cast(tmp) : null;
            }
        }

        return file;
    }

    @Override
    protected void addContent(ArtifactContext context, Node parent, String label, InputStream content) throws IOException {
        Node child;
        if (parent instanceof OpenNode) {
            OpenNode on = (OpenNode) parent;
            child = on.peekChild(label);
            if (child == null) {
                child = on.addNode(label);
            }
        } else {
            child = parent.getChild(label);
        }
        if (child != null) {
            putContent(context, child, content, -1);
        } else {
            throw new IOException("Cannot add child [" + label + "] content [" + content + "] on parent node: " + parent);
        }
    }

    @Override
    protected void removeNode(Node parent, String child) throws IOException {
        final Node node = parent.getChild(child);

        if(fileContentStore != null){
            try {
                if (node != null) {
                    final Node sl = parent.getChild(child + SHA1 + LOCAL);
                    if (sl != null) {
                        fileContentStore.removeFile(sl);
                    }
                    final Node origin = parent.getChild(child + ORIGIN);
                    if (origin != null) {
                        fileContentStore.removeFile(origin);
                    }
                    final Node descriptor = Configuration.getResolvers(this).descriptor(node);
                    if (descriptor != null) {
                        fileContentStore.removeFile(descriptor);
                    }
                }
            } catch (Exception ignored) {
            }
        }

        try {
            super.removeNode(parent, child);
        } finally {
            if (node != null) {
                fileContentStore.removeFile(node);
            }
        }
    }

    @Override
    protected Boolean checkSHA(Node artifact) throws IOException {
        Boolean result = super.checkSHA(artifact);
        if (result == null && artifact.isRemote() == false 
                && getCache() != null && NodeUtils.isAncestor(getCache(), artifact)) {
            Node sha = artifact.getChild(SHA1 + LOCAL);
            if (sha != null) {
                // fileContentStore cannot be null if we have a cache
                File shaFile = fileContentStore.getFile(sha);
                if (shaFile.exists())
                    return checkSHA(artifact, IOUtils.toInputStream(shaFile));
            }
        }
        return result;
    }

    @Override
    public String toString() {
        return "RootRepositoryManager: " + fileContentStore;
    }

    public boolean hasMavenRepository() {
        for(CmrRepository root : getRepositories()){
            if(root.isMaven())
                return true;
        }
        return false;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy