Package org.apache.ivyde.eclipse.cpcontainer

Source Code of org.apache.ivyde.eclipse.cpcontainer.IvyResolveJob

/*
*  Licensed to the Apache Software Foundation (ASF) under one or more
*  contributor license agreements.  See the NOTICE file distributed with
*  this work for additional information regarding copyright ownership.
*  The ASF licenses this file to You 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.apache.ivyde.eclipse.cpcontainer;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.ivy.Ivy;
import org.apache.ivy.core.cache.DefaultRepositoryCacheManager;
import org.apache.ivy.core.cache.RepositoryCacheManager;
import org.apache.ivy.core.event.IvyEvent;
import org.apache.ivy.core.event.IvyListener;
import org.apache.ivy.core.event.download.EndArtifactDownloadEvent;
import org.apache.ivy.core.event.download.PrepareDownloadEvent;
import org.apache.ivy.core.event.download.StartArtifactDownloadEvent;
import org.apache.ivy.core.event.resolve.EndResolveDependencyEvent;
import org.apache.ivy.core.event.resolve.StartResolveDependencyEvent;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DefaultArtifact;
import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.report.ArtifactDownloadReport;
import org.apache.ivy.core.report.ResolveReport;
import org.apache.ivy.core.resolve.DownloadOptions;
import org.apache.ivy.core.resolve.ResolveOptions;
import org.apache.ivy.core.retrieve.RetrieveOptions;
import org.apache.ivy.plugins.report.XmlReportParser;
import org.apache.ivy.plugins.repository.TransferEvent;
import org.apache.ivy.plugins.repository.TransferListener;
import org.apache.ivy.util.Message;
import org.apache.ivyde.eclipse.IvyPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.MultiStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.JavaCore;

/**
* Eclipse classpath container that will contain the ivy resolved entries.
*/
public class IvyResolveJob extends Job implements TransferListener, IvyListener {
    long _expectedTotalLength = 1;

    long _currentLength = 0;

    private IProgressMonitor _monitor;

    private IProgressMonitor _dlmonitor;

    private boolean _usePreviousResolveIfExist;

    private int _workPerArtifact = 100;

    private boolean _notify;

    final Ivy ivy;

    final IvyClasspathContainerConfiguration conf;

    private final IvyClasspathContainer container;

    final ModuleDescriptor md;

    public IvyResolveJob(IvyClasspathContainer container, boolean usePreviousResolveIfExist,
            boolean notify, IvyClasspathContainerConfiguration conf, Ivy ivy, ModuleDescriptor md) {
        super("Resolve "
                + (conf.getJavaProject() == null ? "" : conf.getJavaProject().getProject()
                        .getName()
                        + "/") + conf.ivyXmlPath + " dependencies");
        this.container = container;
        this.ivy = ivy;
        this.md = md;
        _usePreviousResolveIfExist = usePreviousResolveIfExist;
        _notify = notify;
        this.conf = conf;
    }

    public void transferProgress(TransferEvent evt) {
        switch (evt.getEventType()) {
            case TransferEvent.TRANSFER_INITIATED:
                _monitor.setTaskName("downloading " + evt.getResource());
                break;
            case TransferEvent.TRANSFER_STARTED:
                _currentLength = 0;
                if (evt.isTotalLengthSet()) {
                    _expectedTotalLength = evt.getTotalLength();
                    _dlmonitor.beginTask("downloading " + evt.getResource(), 100);
                }
                break;
            case TransferEvent.TRANSFER_PROGRESS:
                if (_expectedTotalLength > 1) {
                    _currentLength += evt.getLength();
                    _dlmonitor.worked((int) (_currentLength * 100 / _expectedTotalLength));
                    _monitor.subTask((_currentLength / 1024) + " / "
                            + (_expectedTotalLength / 1024) + "kB");
                }
                break;
            default:
        }
    }

    public void progress(IvyEvent event) {
        if (event instanceof TransferEvent) {
            if (_dlmonitor != null) {
                transferProgress((TransferEvent) event);
            }
        } else if (event instanceof PrepareDownloadEvent) {
            PrepareDownloadEvent pde = (PrepareDownloadEvent) event;
            Artifact[] artifacts = pde.getArtifacts();
            if (artifacts.length > 0) {
                _workPerArtifact = 1000 / artifacts.length;
            }
        } else if (event instanceof StartArtifactDownloadEvent) {
            StartArtifactDownloadEvent evt = (StartArtifactDownloadEvent) event;
            _monitor.setTaskName("downloading " + evt.getArtifact());
            if (_dlmonitor != null) {
                _dlmonitor.done();
            }
            _dlmonitor = new SubProgressMonitor(_monitor, _workPerArtifact);
        } else if (event instanceof EndArtifactDownloadEvent) {
            if (_dlmonitor != null) {
                _dlmonitor.done();
            }
            _monitor.subTask(" ");
            _dlmonitor = null;
        } else if (event instanceof StartResolveDependencyEvent) {
            StartResolveDependencyEvent ev = (StartResolveDependencyEvent) event;
            _monitor.subTask("resolving " + ev.getDependencyDescriptor().getDependencyRevisionId());
        } else if (event instanceof EndResolveDependencyEvent) {
            _monitor.subTask(" ");
        }
    }

    protected IStatus run(IProgressMonitor monitor) {
        Message.info("resolving dependencies of " + conf.ivyXmlPath);
        _monitor = monitor;
        final IStatus[] status = new IStatus[1];
        final IClasspathEntry[][] classpathEntries = new IClasspathEntry[1][];

        Thread resolver = new Thread() {
            public void run() {
                ivy.pushContext();
                ivy.getEventManager().addIvyListener(IvyResolveJob.this);

                _monitor.beginTask("resolving dependencies", 1000);
                _monitor.setTaskName("resolving dependencies...");

                String[] confs;
                Collection/* <ArtifactDownloadReport> */all;
                List problemMessages;

                // context Classloader hook for commonlogging used by httpclient
                ClassLoader old = Thread.currentThread().getContextClassLoader();
                Thread.currentThread().setContextClassLoader(IvyResolveJob.class.getClassLoader());
                try {

                    if (_usePreviousResolveIfExist) {
                        if (conf.confs.size() == 1 && "*".equals(conf.confs.get(0))) {
                            confs = md.getConfigurationsNames();
                        } else {
                            confs = (String[]) conf.confs.toArray(new String[conf.confs.size()]);
                        }

                        all = new LinkedHashSet();

                        problemMessages = new ArrayList();
                        // we check if all required configurations have been
                        // resolved
                        for (int i = 0; i < confs.length; i++) {
                            File report = ivy.getResolutionCacheManager()
                                    .getConfigurationResolveReportInCache(
                                        ResolveOptions.getDefaultResolveId(md), confs[i]);
                            boolean resolved = false;
                            if (report.exists()) {
                                // found a report, try to parse it.
                                try {
                                    XmlReportParser parser = new XmlReportParser();
                                    parser.parse(report);
                                    all.addAll(Arrays.asList(parser.getArtifactReports()));
                                    resolved = true;
                                } catch (ParseException e) {
                                    Message.info("\n\nIVYDE: Error while parsing the report "
                                            + report + ". Falling back by doing a resolve again.");
                                    // it fails, so let's try resolving
                                }
                            }
                            if (!resolved) {
                                // no resolve previously done for at least
                                // one conf... we do it now
                                Message.info("\n\nIVYDE: previous resolve of "
                                        + md.getModuleRevisionId().getModuleId()
                                        + " doesn't contain enough data: resolving again\n");
                                ResolveReport r = ivy.resolve(md, new ResolveOptions()
                                        .setConfs((String[]) conf.confs
                                                .toArray(new String[conf.confs.size()])));
                                all.addAll(Arrays.asList(r.getArtifactsReports(null, false)));
                                confs = r.getConfigurations();
                                problemMessages.addAll(r.getAllProblemMessages());
                                maybeRetrieve(md, confs);

                                break;
                            }
                        }
                    } else {
                        Message.info("\n\nIVYDE: calling resolve on " + conf.ivyXmlPath + "\n");
                        ResolveReport report = ivy.resolve(md, new ResolveOptions()
                                .setConfs((String[]) conf.confs.toArray(new String[conf.confs
                                        .size()])));
                        problemMessages = report.getAllProblemMessages();
                        all = new LinkedHashSet(Arrays.asList(report.getArtifactsReports(null,
                            false)));
                        confs = report.getConfigurations();

                        if (_monitor.isCanceled()) {
                            status[0] = Status.CANCEL_STATUS;
                            return;
                        }

                        maybeRetrieve(md, confs);
                    }

                    warnIfDuplicates(all);

                    classpathEntries[0] = artifacts2ClasspathEntries(all);
                } catch (ParseException e) {
                    String errorMsg = "Error while parsing the ivy file " + conf.ivyXmlPath + "\n"
                            + e.getMessage();
                    Message.error(errorMsg);
                    status[0] = new Status(IStatus.ERROR, IvyPlugin.ID, IStatus.ERROR, errorMsg, e);
                    return;
                } catch (Exception e) {
                    String errorMsg = "Error while resolving dependencies for " + conf.ivyXmlPath
                            + "\n" + e.getMessage();
                    Message.error(errorMsg);
                    status[0] = new Status(IStatus.ERROR, IvyPlugin.ID, IStatus.ERROR, errorMsg, e);
                    return;
                } finally {
                    Thread.currentThread().setContextClassLoader(old);
                    _monitor.done();
                    ivy.getEventManager().removeIvyListener(IvyResolveJob.this);
                }

                if (!problemMessages.isEmpty()) {
                    MultiStatus multiStatus = new MultiStatus(IvyPlugin.ID, IStatus.ERROR,
                            "Impossible to resolve dependencies of " + md.getModuleRevisionId(),
                            null);
                    for (Iterator iter = problemMessages.iterator(); iter.hasNext();) {
                        multiStatus.add(new Status(IStatus.ERROR, IvyPlugin.ID, (String) iter
                                .next()));
                    }
                    status[0] = multiStatus;
                    return;
                }

                status[0] = Status.OK_STATUS;
            }
        };

        resolver.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable e) {
                status[0] = new Status(IStatus.ERROR, IvyPlugin.ID, IStatus.ERROR,
                        "The resolve job has unexpectedly stopped", e);
            }
        });

        try {
            resolver.start();
            while (true) {
                try {
                    resolver.join(100);
                } catch (InterruptedException e) {
                    ivy.interrupt(resolver);
                    return Status.CANCEL_STATUS;
                }
                synchronized (status) { // ensure proper sharing of done var
                    if (status[0] != null || !resolver.isAlive()) {
                        break;
                    }
                }
                if (_monitor.isCanceled()) {
                    ivy.interrupt(resolver);
                    return Status.CANCEL_STATUS;
                }
            }
            if (status[0] == Status.OK_STATUS) {
                container.updateClasspathEntries(_notify, classpathEntries[0]);
            }
            return status[0];
        } finally {
            container.job = null;
            IvyPlugin.log(IStatus.INFO, "resolved dependencies of " + conf.ivyXmlPath, null);
        }
    }

    /**
     * Trigger a warn if there are duplicates entries due to configuration conflict.
     * <p>
     * TODO: the algorithm can be more clever and find which configuration are conflicting.
     *
     * @param all
     *            the resolved artifacts
     */
    private void warnIfDuplicates(Collection/* <ArtifactDownloadReport> */all) {
        ArtifactDownloadReport[] reports = (ArtifactDownloadReport[]) all
                .toArray(new ArtifactDownloadReport[all.size()]);
        Set duplicates = new HashSet();
        for (int i = 0; i < reports.length - 1; i++) {
            if (accept(reports[i].getArtifact())) {
                ModuleRevisionId mrid1 = reports[i].getArtifact().getModuleRevisionId();
                for (int j = i + 1; j < reports.length; j++) {
                    if (accept(reports[j].getArtifact())) {
                        ModuleRevisionId mrid2 = reports[j].getArtifact().getModuleRevisionId();
                        if (mrid1.getModuleId().equals(mrid2.getModuleId())
                                && !mrid1.getRevision().equals(mrid2.getRevision())) {
                            duplicates.add(mrid1.getModuleId());
                            break;
                        }
                    }
                }
            }
        }
        if (!duplicates.isEmpty()) {
            StringBuffer buffer = new StringBuffer(
                    "There are some duplicates entries due to conflicts between the resolved configurations "
                            + conf.confs);
            buffer.append(":\n  - ");
            Iterator it = duplicates.iterator();
            while (it.hasNext()) {
                buffer.append(it.next());
                if (it.hasNext()) {
                    buffer.append("\n  - ");
                }
            }
            ivy.getLoggerEngine().log(buffer.toString(), Message.MSG_WARN);
        }
    }

    private void maybeRetrieve(ModuleDescriptor md, String[] confs) throws IOException {
        if (conf.getInheritedDoRetrieve()) {
            String pattern = conf.javaProject.getProject().getLocation().toPortableString() + "/"
                    + conf.getInheritedRetrievePattern();
            _monitor.setTaskName("retrieving dependencies in " + pattern);
            ivy.retrieve(md.getModuleRevisionId(), pattern, new RetrieveOptions().setConfs(confs));
        }
    }

    private IClasspathEntry[] artifacts2ClasspathEntries(Collection all) {
        IClasspathEntry[] classpathEntries;
        Collection paths = new LinkedHashSet();
        for (Iterator iter = all.iterator(); iter.hasNext();) {
            ArtifactDownloadReport artifact = (ArtifactDownloadReport) iter.next();
            if (artifact.getLocalFile() != null && accept(artifact.getArtifact())) {
                Path classpathArtifact = new Path(artifact.getLocalFile().getAbsolutePath());
                Path sourcesArtifact = getSourcesArtifactPath(artifact, all);
                Path javadocArtifact = getJavadocArtifactPath(artifact, all);
                paths.add(JavaCore.newLibraryEntry(classpathArtifact, getSourceAttachment(
                    classpathArtifact, sourcesArtifact), getSourceAttachmentRoot(classpathArtifact,
                    sourcesArtifact), null, getExtraAttribute(classpathArtifact, javadocArtifact),
                    false));
            }
        }
        classpathEntries = (IClasspathEntry[]) paths.toArray(new IClasspathEntry[paths.size()]);

        return classpathEntries;
    }

    private Path getSourcesArtifactPath(ArtifactDownloadReport adr, Collection all) {
        Artifact artifact = adr.getArtifact();
        _monitor.subTask("searching sources for " + artifact);
        for (Iterator iter = all.iterator(); iter.hasNext();) {
            ArtifactDownloadReport otherAdr = (ArtifactDownloadReport) iter.next();
            Artifact a = otherAdr.getArtifact();
            if (otherAdr.getLocalFile() != null
                    && isSourceArtifactName(artifact.getName(), a.getName())
                    && a.getId().getRevision().equals(artifact.getId().getRevision())
                    && isSources(a)) {
                return new Path(otherAdr.getLocalFile().getAbsolutePath());
            }
        }
        if (shouldTestNonDeclaredSources()) {
            return getMetaArtifactPath(adr, "source", "sources");
        } else {
            return null;
        }
    }

    private Path getJavadocArtifactPath(ArtifactDownloadReport adr, Collection all) {
        Artifact artifact = adr.getArtifact();
        _monitor.subTask("searching javadoc for " + artifact);
        for (Iterator iter = all.iterator(); iter.hasNext();) {
            ArtifactDownloadReport otherAdr = (ArtifactDownloadReport) iter.next();
            Artifact a = otherAdr.getArtifact();
            if (otherAdr.getLocalFile() != null
                    && isJavadocArtifactName(artifact.getName(), a.getName())
                    && a.getModuleRevisionId().equals(artifact.getModuleRevisionId())
                    && a.getId().equals(artifact.getId()) && isJavadoc(a)) {
                return new Path(otherAdr.getLocalFile().getAbsolutePath());
            }
        }
        if (shouldTestNonDeclaredJavadocs()) {
            return getMetaArtifactPath(adr, "javadoc", "javadoc");
        } else {
            return null;
        }
    }

    /**
     * meta artifact (source or javadoc) not found in resolved artifacts, try to see if a non
     * declared one is available
     */
    private Path getMetaArtifactPath(ArtifactDownloadReport adr, String metaType,
            String metaClassifier) {
        Artifact artifact = adr.getArtifact();
        Map extraAtt = new HashMap(artifact.getExtraAttributes());
        extraAtt.put("classifier", metaClassifier);
        Artifact metaArtifact = new DefaultArtifact(artifact.getModuleRevisionId(), artifact
                .getPublicationDate(), artifact.getName(), metaType, "jar", extraAtt);
        RepositoryCacheManager cache = ivy.getSettings()
                .getResolver(artifact.getModuleRevisionId()).getRepositoryCacheManager();
        if (cache instanceof DefaultRepositoryCacheManager) {
            File metaArtifactFile = ((DefaultRepositoryCacheManager) cache)
                    .getArchiveFileInCache(metaArtifact);
            File attempt = new File(metaArtifactFile.getAbsolutePath() + ".notfound");
            if (metaArtifactFile.exists()) {
                return new Path(metaArtifactFile.getAbsolutePath());
            } else if (attempt.exists()) {
                return null;
            } else {
                Message.info("checking " + metaType + " for " + artifact);
                ivy.getResolveEngine().download(metaArtifact, new DownloadOptions());
                if (metaArtifactFile.exists()) {
                    return new Path(metaArtifactFile.getAbsolutePath());
                }
                // meta artifact not found, we store this information to avoid other
                // attempts later
                Message.info(metaType + " not found for " + artifact);
                try {
                    attempt.getParentFile().mkdirs();
                    attempt.createNewFile();
                } catch (IOException e) {
                    Message.error("impossible to create attempt file " + attempt + ": " + e);
                }
                return null;
            }
        }
        Message.info("checking " + metaType + " for " + artifact);
        ArtifactDownloadReport metaAdr = ivy.getResolveEngine().download(metaArtifact,
            new DownloadOptions());
        if (metaAdr.getLocalFile() != null && metaAdr.getLocalFile().exists()) {
            return new Path(metaAdr.getLocalFile().getAbsolutePath());
        }
        Message.info(metaType + " not found for " + artifact);
        Message
                .verbose("Attempt not stored in cache because a non Default cache implementation is used.");
        return null;
    }

    private IPath getSourceAttachment(Path classpathArtifact, Path sourcesArtifact) {
        IPath sourceAttachment = IvyPlugin.getDefault().getPackageFragmentExtraInfo()
                .getSourceAttachment(classpathArtifact);
        if (sourceAttachment == null) {
            sourceAttachment = sourcesArtifact;
        }
        return sourceAttachment;
    }

    private IPath getSourceAttachmentRoot(Path classpathArtifact, Path sourcesArtifact) {
        IPath sourceAttachment = IvyPlugin.getDefault().getPackageFragmentExtraInfo()
                .getSourceAttachmentRoot(classpathArtifact);
        if (sourceAttachment == null && sourcesArtifact != null) {
            sourceAttachment = sourcesArtifact;
        }
        return sourceAttachment;
    }

    private IClasspathAttribute[] getExtraAttribute(Path classpathArtifact, Path javadocArtifact) {
        List result = new ArrayList();
        URL url = IvyPlugin.getDefault().getPackageFragmentExtraInfo().getDocAttachment(
            classpathArtifact);

        if (url == null) {
            Path path = javadocArtifact;
            if (path != null) {
                String u;
                try {
                    u = "jar:" + path.toFile().toURI().toURL().toExternalForm() + "!/";
                    try {
                        url = new URL(u);
                    } catch (MalformedURLException e) {
                        // this should not happen
                        IvyPlugin.log(IStatus.ERROR,
                            "The jar URL for the javadoc is not formed correctly " + u, e);
                    }
                } catch (MalformedURLException e) {
                    // this should not happen
                    IvyPlugin.log(IStatus.ERROR, "The path has not a correct URL: " + path, e);
                }
            }
        }

        if (url != null) {
            result.add(JavaCore.newClasspathAttribute(
                IClasspathAttribute.JAVADOC_LOCATION_ATTRIBUTE_NAME, url.toExternalForm()));
        }
        return (IClasspathAttribute[]) result.toArray(new IClasspathAttribute[result.size()]);
    }

    public boolean shouldTestNonDeclaredSources() {
        return true; // TODO: add settings for that
    }

    public boolean shouldTestNonDeclaredJavadocs() {
        return true; // TODO: add settings for that
    }

    public boolean isJavadocArtifactName(String jar, String javadoc) {
        return isArtifactName(jar, javadoc, conf.getInheritedJavadocSuffixes());
    }

    public boolean isSourceArtifactName(String jar, String source) {
        return isArtifactName(jar, source, conf.getInheritedSourceSuffixes());
    }

    private boolean isArtifactName(String jar, String name, Collection/* <String> */suffixes) {
        if (name.equals(jar)) {
            return true;
        }
        Iterator it = suffixes.iterator();
        while (it.hasNext()) {
            if (name.equals(jar + it.next())) {
                return true;
            }
        }
        return false;
    }

    /**
     * Check if the artifact is an artifact which can be added to the classpath container
     *
     * @param artifact
     *            the artifact to check
     * @return <code>true</code> if the artifact can be added
     */
    public boolean accept(Artifact artifact) {
        return conf.getInheritedAcceptedTypes().contains(artifact.getType())
                && !conf.getInheritedSourceTypes().contains(artifact.getType())
                && !conf.getInheritedJavadocTypes().contains(artifact.getType());
    }

    public boolean isSources(Artifact artifact) {
        return conf.getInheritedSourceTypes().contains(artifact.getType());
    }

    public boolean isJavadoc(Artifact artifact) {
        return conf.getInheritedJavadocTypes().contains(artifact.getType());
    }

}
TOP

Related Classes of org.apache.ivyde.eclipse.cpcontainer.IvyResolveJob

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.