Package org.apache.felix.sigil.ivy

Source Code of org.apache.felix.sigil.ivy.SigilResolver

/*
* 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.felix.sigil.ivy;

import static java.lang.String.format;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;
import java.util.jar.Manifest;

import org.apache.felix.sigil.common.config.BldFactory;
import org.apache.felix.sigil.common.config.IRepositoryConfig;
import org.apache.felix.sigil.common.model.IModelElement;
import org.apache.felix.sigil.common.model.ModelElementFactory;
import org.apache.felix.sigil.common.model.eclipse.ISigilBundle;
import org.apache.felix.sigil.common.model.osgi.IBundleModelElement;
import org.apache.felix.sigil.common.model.osgi.IRequiredBundle;
import org.apache.felix.sigil.common.osgi.VersionRange;
import org.apache.felix.sigil.common.repository.IResolution;
import org.apache.felix.sigil.common.repository.ResolutionException;
import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
import org.apache.ivy.core.module.id.ModuleRevisionId;
import org.apache.ivy.core.resolve.ResolveData;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.url.URLResource;
import org.apache.ivy.plugins.resolver.BasicResolver;
import org.apache.ivy.plugins.resolver.util.ResolvedResource;
import org.apache.ivy.util.FileUtil;

/**
* This resolver is able to work with Sigil repositories.
* It does not allow publishing.
*/
public class SigilResolver extends BasicResolver implements IBldResolver
{
    /** the sigil-injected dependency organisation name */
    static final String ORG_SIGIL = "sigil";

    private IBldResolver resolver;
    private String config;
    private boolean extractBCP = false;
    private Map<String, Resource> resourcesCache = new HashMap<String, Resource>();

    /**
     * no-args constructor required by Ivy.
     *
     * XXX: It doesn't currently seem to matter that Ivy instantiates this many times,
     * since SigilParser caches it, and the ProjectRepositoryProvider static cache
     * prevents it being re-loaded unnecessarily.
     *
     * If this should become a problem, then we will need to delegate to a singleton instance,
     * as we have done in SigilParser.
     */
    public SigilResolver()
    {
    }

    public void setConfig(String config)
    {
        this.config = config;
    }

    /**
     * if true, resolver extracts any jars embedded in Bundle-ClassPath.
     */
    public void setExtractBCP(String extract)
    {
        this.extractBCP = Boolean.parseBoolean(extract);
    }

    private IBldResolver getResolver()
    {
        if (resolver == null)
        {
            if (config == null)
            {
                throw new Error(
                    "SigilResolver: not configured. Specify config=\"PATH\" in ivysettings.xml.");
            }
            try
            {
                URI uri;
                File file = new File(config);

                if (file.isAbsolute())
                {
                    uri = file.toURI();
                }
                else
                {
                    URI cwd = new File(".").toURI();
                    uri = cwd.resolve(config);
                }

                IRepositoryConfig project = BldFactory.getConfig(uri);
                resolver = new BldResolver(project);
            }
            catch (IOException e)
            {
                throw new Error("SigilResolver: failed to configure: " + e);
            }
        }
        return resolver;
    }

    public IResolution resolveOrFail(IModelElement element, boolean transitive)
        throws ResolutionException
    {
        return getResolver().resolveOrFail(element, transitive);
    }

    public IResolution resolve(IModelElement element, boolean transitive)
    {
        return getResolver().resolve(element, transitive);
    }

    public String getTypeName()
    {
        return "sigil";
    }

    /*
     * synthesize an ivy descriptor for a Sigil dependency. called after Sigil
     * resolution, so descriptor already contains resolved version.
     */
    public ResolvedResource findIvyFileRef(DependencyDescriptor dd, ResolveData data)
    {
        ResolvedResource ref = null;

        ModuleRevisionId id = dd.getDependencyRevisionId();

        if (!id.getOrganisation().equals(ORG_SIGIL))
        {
            return null;
        }

        ISigilBundle bundle = resolve(id);
        if (bundle == null)
        {
            Log.error("Failed to find bundle for module " + id);
            return null;
        }

        String symbolicName = id.getName();
        String version = bundle.getVersion().toString();

        Resource res = new SigilIvy(extractBCP ? bundle : null, symbolicName, version);
        ref = new ResolvedResource(res, version);

        Log.debug(format("findIvyFileRef: dd=%s => ref=%s", dd, ref));
        return ref;
    }

    @Override
    protected ResolvedResource findArtifactRef(Artifact artifact, Date date)
    {
        String name = artifact.getName();
        ModuleRevisionId id = artifact.getModuleRevisionId();

        if (!id.getOrganisation().equals(ORG_SIGIL))
        {
            return null;
        }

        ISigilBundle bundle = resolve(id);
        if (bundle == null)
        {
            return null;
        }

        IBundleModelElement info = bundle.getBundleInfo();
        URI uri = info.getUpdateLocation();
        Resource res = null;

        try
        {
            URL url = (uri != null) ? uri.toURL() : bundle.getLocation().toURI().toURL();
            if (name.contains("!"))
            {
                String[] split = name.split("!");
                url = new URL("jar:" + url + "!/" + split[1] + "." + artifact.getExt());
            }
            res = new URLResource(url);
        }
        catch (MalformedURLException e)
        {
            System.out.println("Oops! " + e);
        }

        String version = bundle.getVersion().toString();
        ResolvedResource ref = new ResolvedResource(res, version);

        Log.debug(format("findArtifactRef: artifact=%s, date=%s => ref=%s", artifact,
            date, ref));
        return ref;
    }

    private ISigilBundle resolve(ModuleRevisionId id)
    {
        String revision = id.getRevision();
        String range = revision;

        if (revision.indexOf(',') < 0)
        {
            // SigilParser has already resolved the revision from the import
            // version range.
            // We now need to locate the same bundle to get its download URL.
            // We must use an OSGi version range to specify the exact version,
            // otherwise it will resolve to "specified version or above".
            range = "[" + revision + "," + revision + "]";
        }

        IRequiredBundle bundle = ModelElementFactory.getInstance().newModelElement(
            IRequiredBundle.class);
        bundle.setSymbolicName(id.getName());
        bundle.setVersions(VersionRange.parseVersionRange(range));

        Log.verbose("searching for " + bundle);

        try
        {
            IResolution resolution = resolveOrFail(bundle, false);
            ISigilBundle[] bundles = resolution.getBundles().toArray(new ISigilBundle[0]);
            if (bundles.length == 1)
            {
                return bundles[0];
            }
        }
        catch (ResolutionException e)
        {
            Log.warn(e.getMessage());
            return null;
        }
        return null;
    }

    /*
     * Implement BasicResolver abstract methods
     */

    @Override
    protected long get(Resource res, File dest) throws IOException
    {
        FileUtil.copy(res.openStream(), dest, null);
        long len = res.getContentLength();
        Log.debug(format("get(%s, %s) = %d", res, dest, len));
        return len;
    }

    @Override
    public Resource getResource(String source) throws IOException
    {
        source = encode(source);
        Resource res = resourcesCache.get(source);
        if (res == null)
        {
            res = new URLResource(new URL(source));
            resourcesCache.put(source, res);
        }
        Log.debug(format("SIGIL: getResource(%s) = %d", source, res));
        return res;
    }

    private static String encode(String source)
    {
        return source.trim().replaceAll(" ", "%20");
    }

    @SuppressWarnings("unchecked")
    @Override
    protected Collection findNames(Map tokenValues, String token)
    {
        throw new Error("SigilResolver: findNames not supported.");
    }

    public void publish(Artifact artifact, File src, boolean overwrite)
        throws IOException
    {
        throw new Error("SigilResolver: publish not supported.");
    }

    public void beginPublishTransaction(ModuleRevisionId module, boolean overwrite)
        throws IOException
    {
        // stop publish attempts being silently ignored.
        throw new Error("SigilResolver: publish not supported.");
    }

    /*
     * Synthesize a virtual ivy file for a Sigil dependency.
     */
    static class SigilIvy implements Resource
    {
        private StringBuilder content = new StringBuilder();
        private String name;
        private boolean exists = true;

        private SigilIvy()
        {
        }

        public SigilIvy(ISigilBundle bundle, String module, String rev)
        {
            this.name = "sigil!" + module + "#" + rev;

            String org = ORG_SIGIL;
            // FIXME: make status and pub configurable
            String status = "release";
            String pub = "20080912162859";

            content.append("<ivy-module version=\"1.0\">\n");

            content.append(format(
                "<info organisation=\"%s\" module=\"%s\" revision=\"%s\" status=\"%s\" publication=\"%s\"/>\n",
                org, module, rev, status, pub));

            String bcp = readBundleClassPath(bundle);
            if (bcp != null)
            {
                content.append("<publications>\n");
                for (String j : bcp.split(",\\s*"))
                {
                    if (j.equals("."))
                    {
                        content.append("<artifact/>\n");
                    }
                    else if (j.endsWith(".jar") && bundleContains(bundle, j))
                    {
                        j = j.substring(0, j.length() - 4);
                        content.append(format("<artifact name=\"%s!%s\"/>\n", module, j));
                    }
                }
                content.append("</publications>\n");
            }

            // TODO: add dependencies?
            // <dependencies>
            // <dependency org="org.apache" name="log4j" rev="1.2.12"
            // revConstraint="[1.2,1.3)"/>
            // </dependencies>

            content.append("</ivy-module>\n");
        }

        private boolean bundleContains(ISigilBundle bundle, String j)
        {
            InputStream is = null;
            try
            {
                URL url = getURL(bundle);
                is = url.openStream();
                JarInputStream js = new JarInputStream(is, false);
                JarEntry entry;
                while ((entry = js.getNextJarEntry()) != null)
                {
                    if (j.equals(entry.getName()))
                    {
                        return true;
                    }
                }
            }
            catch (IOException e)
            {
            }
            finally
            {
                if (is != null)
                {
                    try
                    {
                        is.close();
                    }
                    catch (IOException e2)
                    {
                    }
                }
            }
            return false;
        }

        private String readBundleClassPath(ISigilBundle bundle)
        {
            if (bundle == null)
                return null;

            InputStream is = null;
            try
            {
                URL url = getURL(bundle);
                is = url.openStream();
                JarInputStream js = new JarInputStream(is, false);
                Manifest m = js.getManifest();
                if (m != null)
                    return (String) m.getMainAttributes().getValue("Bundle-ClassPath");
            }
            catch (IOException e)
            {
            }
            finally
            {
                if (is != null)
                {
                    try
                    {
                        is.close();
                    }
                    catch (IOException e2)
                    {
                    }
                }
            }

            return null;
        }

        private URL getURL(ISigilBundle bundle) throws MalformedURLException
        {
            URI uri = bundle.getBundleInfo().getUpdateLocation();
            if (uri != null)
            {
                return uri.toURL();
            }
            else
            {
                File path = bundle.getLocation();
                if (path == null)
                {
                    throw new NullPointerException("Missing location for "
                        + bundle.getSymbolicName());
                }
                return path.toURI().toURL();
            }
        }

        public String toString()
        {
            return "SigilIvy[" + name + "]";
        }

        // clone is used to read checksum files
        // so clone(getName() + ".sha1").exists() should be false
        public Resource clone(String cloneName)
        {
            Log.debug("SigilIvy: clone: " + cloneName);
            SigilIvy clone = new SigilIvy();
            clone.name = cloneName;
            clone.exists = false;
            return clone;
        }

        public boolean exists()
        {
            return exists;
        }

        public long getContentLength()
        {
            return content.length();
        }

        public long getLastModified()
        {
            // TODO Auto-generated method stub
            Log.debug("NOT IMPLEMENTED: getLastModified");
            return 0;
        }

        public String getName()
        {
            return name;
        }

        public boolean isLocal()
        {
            return false;
        }

        @SuppressWarnings("deprecation")
        public InputStream openStream() throws IOException
        {
            return new java.io.StringBufferInputStream(content.toString());
        }
    }
}
TOP

Related Classes of org.apache.felix.sigil.ivy.SigilResolver

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.