Xavier Hanin wrote:
> 
> On 10/30/07, John Rasmussen <[EMAIL PROTECTED]> wrote:
>>
>> I have extended org.apache.ivy.plugins.repository.AbstractRepository
>> in order to build modules directly from source.
> 
> Interesting use case, feel free to share more about that with the
> community
> if you can.
> 
> 

The following might be useful.
Please notice that to build a module from source requires that all module
are structured in a uniform way and that the implementation highly depends
on that structure.
Thus a lot of details regarding structure is skipped, because that depends
on your structure.

First of all, this will only work in Ivy 2.0+
For Ivy 1.4.1, see https://issues.apache.org/jira/browse/IVY-448

Some of the examples below is in "it works" state and not in "brilliant"
state.
Feel free to comment on that, if some can be changed or made more simple.


First the ivysettings.xml is updated like:
<ivysettings>
    <typedef name="my-resolver" classname="mypackage.MyResolver"/>
    <typedef name="my-latest-strategy"
classname="mypackage.MyLatestStrategy"/>
    <latest-strategies>
        <my-latest-strategy/>
    </latest-strategies>
    <conflict-managers>
        <latest-cm name="my-latest-strategy" latest="my-latest-strategy"/>
    </conflict-managers>
    <resolvers>
        <my-resolver name="my-resolver" changingPattern="head"
changingMatcher="exact"
                      allownomd="false" checksums=""
latest="my-latest-strategy">
            <ivy
pattern="svn://myhost/[organisation]/[module]/[revision]/ivy-[module]-[revision].xml"/>
            <artifact
pattern="svn://myhost/[organisation]/[module]/[revision]/[artifact]/[artifact]-[revision].[type]"/>
        </my-resolver>
    </resolvers>
    <modules>
        <module organisation="myorg" name=".*" resolver="my-resolver"
conflict-manager="my-latest-strategy"/>
    </modules>
</ivysettings>



Two classes are required. The mypackage.MyResolver class builds the module,
and the mypackage.MyLatestStrategy class evicts other revisions than the
"build-from-source" revisions. In the ivysettings that revision is called
'head'.

In order to load these two classes, the classes are inserted into
myIvyPlugin.jar, and the retrieve target in ant looks like:
Notice my.classpath should contain whatever you are using. I have module
revisions stored in Subversion which are retrieved from the repository using
svnkit, which has to be included in my.classpath.
  <path id="my.classpath">
      <pathelement location="myIvyPlugin.jar"/>
      <pathelement location="/path/to/ivy-2.0.0-alpha2-incubating.jar"/> 
  </path>

  <taskdef name="ivy-retrieve" classname="org.apache.ivy.ant.IvyRetrieve"
         classpathref="my.classpath" loaderref="ivy.task.loader"/>

  <target name="retrieve">
      <ivy-retrieve/>
  </target>



Now for the java stuff

First MyResolver, which is simple:
import org.apache.ivy.plugins.resolver.RepositoryResolver;
public class MyResolver extends RepositoryResolver {
    public MyResolver() {
        setRepository(new MyRepository());
    }

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


MyRepository is the next java class.
Please notice, that the put method is unimplemented.
I'm not using Ivy to stored revisions - historical reasons.
Notice the uri cache, otherwise the build process is repeated a couple of
times.

import org.apache.ivy.core.module.descriptor.Artifact;
import org.apache.ivy.plugins.repository.AbstractRepository;
import org.apache.ivy.plugins.repository.Resource;
import org.apache.ivy.plugins.repository.TransferEvent;
import org.apache.ivy.util.Message;

public final class MyRepository extends AbstractRepository {
    private final Map uriMap = new HashMap();
    public final void get(final String source, final File destination)
throws IOException {
        try {
            final RepositoryResource repositoryResource =
getRepositoryResource(source);
            fireTransferInitiated(repositoryResource,
TransferEvent.REQUEST_GET);
            fireTransferStarted();
            repositoryResource.save(destination);
            fireTransferCompleted(repositoryResource.getContentLength());
        } catch (final MyException e) {
            Message.error("Cannot get " + source);
            fireTransferError(e);
        }
    }

    public final Resource getResource(final String source) throws
IOException {
        try {
            final Resource resource = getRepositoryResource(source);
            return resource;
        } catch (final MyException e) {
            Message.error("Cannot get " + source);
            fireTransferError(e);
            throw new IOException("Failed");
        }
    }

    public final List list(final String source) throws IOException {
        try {
            final List list = getRepositoryResource(source).list();
            return list;
        } catch (final MyException e) {
            fireTransferError(e);
            return Collections.EMPTY_LIST;
        }
    }

    public final void put(final Artifact artifact, final File source, final
String destination, final boolean overwrite) throws IOException {
        fireTransferError(new MyException("Put-operation not supported.
Source " + source + ", destination " +
                                           destination));
    }

    private final RepositoryResource getRepositoryResource(final String uri)
throws MyException {
        RepositoryResource repositoryResource = ( RepositoryResource )
uriMap.get(uri);
        if (repositoryResource == null) {
            repositoryResource = MyResource(uri);
            uriMap.put(uri, repositoryResource);
        }
        return repositoryResource;
    }
}


A simple interface
public interface RepositoryResource extends Resource {
    List list();

    void save(final File destination) throws IOException;
}


Given an uri in a format defined in ivysettings, the task is to build the
module from source.
This is done in MyResource, where all module structure details are placed.
Thus this java class does not compile as is. You need to add your details or
return "null" if not used.
I have listed a few "null" methods.
Notice, if a module build more than one artifact, then some caching/"need to
update"
functionality must be implemented somewhere to avoid that the system builds
everything
for each artifact.
The files generated from the build is returned to ivy in the save() method


public final class MyResource implements RepositoryResource {
    public MyResource(final String uri {
        // Build source 
    }

    public final long getContentLength() {
        return getFile().length();
    }

    public final String getName() {
        return uri;
    }

    public final boolean isLocal() {
        return false;
    }

    public final List list() {
        return Collections.EMPTY_LIST;
    }

    public InputStream openStream() throws IOException {
        throw new IOException("openStream unsupported.");
    }

    public final void save(final File destination) throws IOException {
        final File source = getFile();
        final InputStream is = new FileInputStream(source);
        final OutputStream os = new FileOutputStream(destination);
        final byte[] b = new byte[BUFFER_SIZE];
        int size;

        while ((size = is.read(b)) != -1) {
            os.write(b, 0, size);
        }

        os.close();
        is.close();
    }

    private final File getFile() {
          // Return the file being build given the uri
    }
}


Other revisions than "head" should also be handled. That depends on how your
revisions are stored.
I have an svnkit implementation to retrieve from Subversion - historical
reasons.
Today such an implementation might already exists in Ivy??

Most artifacts can be returned "as is".
One exception is the ivy.xml artifact.
The revision in Ivy.xml must correspond to the revision in the dependency
tag, otherwise Ivy complains.
Of course, this depends on how you return your ivy.xml file. (You might
build/deliver an ivy.xml with correct version)
I did a simple OutputStream replace:
And use a replaced os in the save() method BUT only for the ivy.xml
artifact!
 new StringReplaceOutputStream(os, " revision *= *\"[^\"]*\"",
                                                  " revision=\"head\"");

public final class StringReplaceOutputStream extends OutputStream {
    private int index;
    private final OutputStream outputStream;
    private final String regex;
    private final String replacement;
    private final StringBuffer strBuf;

    public StringReplaceOutputStream(final OutputStream outputStream, final
String regex, final String replacement) {
        this.outputStream = outputStream;
        strBuf = new StringBuffer();
        this.regex = regex;
        this.replacement = replacement;
        index = 0;
    }

    public final void close() throws IOException {
        flush();
        outputStream.close();
    }

    public final synchronized void flush() throws IOException {
        writeFromStrBuf(strBuf.length());
        outputStream.flush();
    }

    public final void write(final int b) throws IOException {
        strBuf.append(( char ) (b & 0xFF));
        update();
    }

    public final void write(final byte[] b, final int off, final int len)
throws IOException {
        strBuf.append(new String(b, off, len));
        update();
    }

    private final void update() throws IOException {
        while (index < (strBuf.length() - 1)) {
            if (strBuf.charAt(index) == 0x0D) {
                if (strBuf.charAt(index + 1) == 0x0A) {
                    index++;
                }

                writeFromStrBuf(index + 1);
            } else {
                index++;
            }
        }
    }

    private final void writeFromStrBuf(final int index) throws IOException {
        final String str = strBuf.substring(0, index).toString();
        final String strRep = str.replaceAll(regex, replacement);
        outputStream.write(strRep.getBytes());
        strBuf.delete(0, index);
        this.index = 0;
    }
}


To evict other revisions than "head" add MyLatestStrategy.java
import java.util.Comparator;
import org.apache.ivy.plugins.latest.ArtifactInfo;
import org.apache.ivy.plugins.latest.ComparatorLatestStrategy;
import org.apache.ivy.plugins.latest.LatestRevisionStrategy;


public class MyLatestStrategy extends ComparatorLatestStrategy {
    public MyLatestStrategy() {
        super(new Comparator() {
                private final Comparator comparator = new
LatestRevisionStrategy().COMPARATOR;

                public int compare(Object o1, Object o2) {
                    String rev1 = (( ArtifactInfo ) o1).getRevision();
                    String rev2 = (( ArtifactInfo ) o2).getRevision();

                    if (rev1.startsWith("head")) {
                        return 1;
                    } else if (rev2.startsWith("head")) {
                        return -1;
                    } else {
                        return comparator.compare(o1, o2);
                    }
                }
            });
        setName("my-latest-strategy");
    }
}

-- 
View this message in context: 
http://www.nabble.com/How-to-halt-on-an-unresolved-dependency-tf4717519.html#a13791968
Sent from the ivy-dev mailing list archive at Nabble.com.

Reply via email to