diff --git a/CHANGES.txt b/CHANGES.txt index 1985a176..7e4b297f 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -1,3 +1,5 @@ +- NEW: overwrite attribute in the publish task now let force overwrite of read only files (IVY-83) +- IMPROVE: better error message when publish fails due to readonly destination (IVY-83) - FIX: raise a clean error when a cyclic variable definition is found (IVY-75) - FIX: reinitiliase ant project instance at each new task to avoid using a bad ant project instance in some ide (like netbeans) (IVY-87) - FIX: ivy is now able to use simple ivy files in cache (doesn't need resolver info, use default one if no resolver is given) (IVY-86) diff --git a/src/java/fr/jayasoft/ivy/DependencyResolver.java b/src/java/fr/jayasoft/ivy/DependencyResolver.java index ff06728e..69d14694 100644 --- a/src/java/fr/jayasoft/ivy/DependencyResolver.java +++ b/src/java/fr/jayasoft/ivy/DependencyResolver.java @@ -36,7 +36,7 @@ public interface DependencyResolver { ResolvedModuleRevision getDependency(DependencyDescriptor dd, ResolveData data) throws ParseException; DownloadReport download(Artifact[] artifacts, Ivy ivy, File cache); boolean exists(Artifact artifact); - void publish(Artifact artifact, File src) throws IOException; + void publish(Artifact artifact, File src, boolean overwrite) throws IOException; /** * Reports last resolve failure as Messages diff --git a/src/java/fr/jayasoft/ivy/Ivy.java b/src/java/fr/jayasoft/ivy/Ivy.java index 31ea6eac..b7aa906c 100644 --- a/src/java/fr/jayasoft/ivy/Ivy.java +++ b/src/java/fr/jayasoft/ivy/Ivy.java @@ -1220,7 +1220,19 @@ public class Ivy implements TransferListener { * @return a collection of missing artifacts (those that are not published) * @throws ParseException */ - public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache, String srcArtifactPattern, String resolverName, String srcIvyPattern, boolean validate) throws IOException { + public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache, String srcArtifactPattern, String resolverName, String srcIvyPattern, boolean validate) throws IOException { + return publish(mrid, pubrevision, cache, srcArtifactPattern, resolverName, srcIvyPattern, validate, false); + } + /** + * + * @param pubrevision + * @param resolverName the name of a resolver to use for publication + * @param srcArtifactPattern a pattern to find artifacts to publish with the given resolver + * @param srcIvyPattern a pattern to find ivy file to publish, null if ivy file should not be published + * @return a collection of missing artifacts (those that are not published) + * @throws ParseException + */ + public Collection publish(ModuleRevisionId mrid, String pubrevision, File cache, String srcArtifactPattern, String resolverName, String srcIvyPattern, boolean validate, boolean overwrite) throws IOException { Message.info(":: publishing :: "+mrid); Message.verbose("\tvalidate = "+validate); long start = System.currentTimeMillis(); @@ -1261,13 +1273,13 @@ public class Ivy implements TransferListener { for (Iterator iter = artifactsSet.iterator(); iter.hasNext();) { Artifact artifact = (Artifact) iter.next(); // 1) copy the artifact using src pattern and resolver - if (!publish(artifact, srcArtifactPattern, resolver)) { + if (!publish(artifact, srcArtifactPattern, resolver, overwrite)) { missing.add(artifact); } } if (srcIvyPattern != null) { Artifact artifact = new MDArtifact(md, "ivy", "ivy", "xml"); - if (!publish(artifact, srcIvyPattern, resolver)) { + if (!publish(artifact, srcIvyPattern, resolver, overwrite)) { missing.add(artifact); } } @@ -1275,17 +1287,14 @@ public class Ivy implements TransferListener { return missing; } - private boolean publish(Artifact artifact, String srcArtifactPattern, DependencyResolver resolver) { + private boolean publish(Artifact artifact, String srcArtifactPattern, DependencyResolver resolver, boolean overwrite) throws IOException { File src = new File(IvyPatternHelper.substitute(srcArtifactPattern, artifact)); if (src.exists()) { - try { - resolver.publish(artifact, src); - return true; - } catch (Exception ex) { - Message.error("impossible to publish "+artifact+" with "+resolver.getName()+": "+ex.getMessage()); - } + resolver.publish(artifact, src, overwrite); + return true; + } else { + return false; } - return false; } ///////////////////////////////////////////////////////////////////////// diff --git a/src/java/fr/jayasoft/ivy/ant/IvyPublish.java b/src/java/fr/jayasoft/ivy/ant/IvyPublish.java index 778af4db..11ca535f 100644 --- a/src/java/fr/jayasoft/ivy/ant/IvyPublish.java +++ b/src/java/fr/jayasoft/ivy/ant/IvyPublish.java @@ -40,6 +40,7 @@ public class IvyPublish extends IvyTask { private boolean _publishivy = true; private boolean _warnonmissing = true; private boolean _haltonmissing = true; + private boolean _overwrite = false; public File getCache() { return _cache; @@ -183,7 +184,7 @@ public class IvyPublish extends IvyTask { deliver.execute(); } - Collection missing = ivy.publish(mrid, _pubRevision, _cache, _artifactspattern, _publishResolverName, _publishivy?_srcivypattern:null, doValidate(ivy)); + Collection missing = ivy.publish(mrid, _pubRevision, _cache, _artifactspattern, _publishResolverName, _publishivy?_srcivypattern:null, doValidate(ivy), _overwrite); if (_warnonmissing) { for (Iterator iter = missing.iterator(); iter.hasNext();) { Artifact artifact = (Artifact)iter.next(); @@ -237,4 +238,10 @@ public class IvyPublish extends IvyTask { public void setHaltonmissing(boolean haltonmissing) { _haltonmissing = haltonmissing; } + public boolean isOverwrite() { + return _overwrite; + } + public void setOverwrite(boolean overwrite) { + _overwrite = overwrite; + } } diff --git a/src/java/fr/jayasoft/ivy/repository/Repository.java b/src/java/fr/jayasoft/ivy/repository/Repository.java index 89d9510a..f201f017 100644 --- a/src/java/fr/jayasoft/ivy/repository/Repository.java +++ b/src/java/fr/jayasoft/ivy/repository/Repository.java @@ -13,7 +13,7 @@ import java.util.List; public interface Repository { Resource getResource(String source) throws IOException; void get(String source, File destination) throws IOException; - void put(File source, String destination) throws IOException; + void put(File source, String destination, boolean overwrite) throws IOException; /** * Returns the list of all resources names that can be found in the given * parent. diff --git a/src/java/fr/jayasoft/ivy/repository/file/FileRepository.java b/src/java/fr/jayasoft/ivy/repository/file/FileRepository.java index 262c0f66..a98a72cc 100644 --- a/src/java/fr/jayasoft/ivy/repository/file/FileRepository.java +++ b/src/java/fr/jayasoft/ivy/repository/file/FileRepository.java @@ -46,17 +46,17 @@ public class FileRepository extends AbstractRepository { public void get(String source, File destination) throws IOException { fireTransferInitiated(getResource(source), TransferEvent.REQUEST_GET); - copy(getFile(source), destination); + copy(getFile(source), destination, true); } - public void put(File source, String destination) throws IOException { + public void put(File source, String destination, boolean overwrite) throws IOException { fireTransferInitiated(getResource(destination), TransferEvent.REQUEST_PUT); - copy(source, getFile(destination)); + copy(source, getFile(destination), overwrite); } - private void copy(File src, File destination) throws IOException { + private void copy(File src, File destination, boolean overwrite) throws IOException { try { - FileUtil.copy(src, destination, _progress); + FileUtil.copy(src, destination, _progress, overwrite); } catch (IOException ex) { fireTransferError(ex); throw ex; diff --git a/src/java/fr/jayasoft/ivy/repository/url/URLRepository.java b/src/java/fr/jayasoft/ivy/repository/url/URLRepository.java index fdfe0af5..002a0777 100644 --- a/src/java/fr/jayasoft/ivy/repository/url/URLRepository.java +++ b/src/java/fr/jayasoft/ivy/repository/url/URLRepository.java @@ -52,7 +52,7 @@ public class URLRepository extends AbstractRepository { } } - public void put(File source, String destination) throws IOException { + public void put(File source, String destination, boolean overwrite) throws IOException { throw new UnsupportedOperationException("URL repository is not able to put files for the moment"); } diff --git a/src/java/fr/jayasoft/ivy/resolver/CacheResolver.java b/src/java/fr/jayasoft/ivy/resolver/CacheResolver.java index c5ea52fa..2088acb3 100644 --- a/src/java/fr/jayasoft/ivy/resolver/CacheResolver.java +++ b/src/java/fr/jayasoft/ivy/resolver/CacheResolver.java @@ -103,9 +103,9 @@ public class CacheResolver extends FileSystemResolver { ensureConfigured(); return super.exists(artifact); } - public void publish(Artifact artifact, File src) throws IOException { + public void publish(Artifact artifact, File src, boolean overwrite) throws IOException { ensureConfigured(); - super.publish(artifact, src); + super.publish(artifact, src, overwrite); } diff --git a/src/java/fr/jayasoft/ivy/resolver/ChainResolver.java b/src/java/fr/jayasoft/ivy/resolver/ChainResolver.java index dcf3a1dc..910a1dd7 100644 --- a/src/java/fr/jayasoft/ivy/resolver/ChainResolver.java +++ b/src/java/fr/jayasoft/ivy/resolver/ChainResolver.java @@ -99,11 +99,11 @@ public class ChainResolver extends AbstractResolver { public List getResolvers() { return _chain; } - public void publish(Artifact artifact, File src) throws IOException { + public void publish(Artifact artifact, File src, boolean overwrite) throws IOException { if (_chain.isEmpty()) { throw new IllegalStateException("invalid chain resolver with no sub resolver"); } - ((DependencyResolver)_chain.get(0)).publish(artifact, src); + ((DependencyResolver)_chain.get(0)).publish(artifact, src, overwrite); } public boolean isReturnFirst() { return _returnFirst; diff --git a/src/java/fr/jayasoft/ivy/resolver/DualResolver.java b/src/java/fr/jayasoft/ivy/resolver/DualResolver.java index 26c1597b..892a5639 100644 --- a/src/java/fr/jayasoft/ivy/resolver/DualResolver.java +++ b/src/java/fr/jayasoft/ivy/resolver/DualResolver.java @@ -117,11 +117,11 @@ public class DualResolver extends AbstractResolver { public void setIvyResolver(DependencyResolver ivyResolver) { _ivyResolver = ivyResolver; } - public void publish(Artifact artifact, File src) throws IOException { + public void publish(Artifact artifact, File src, boolean overwrite) throws IOException { if ("ivy".equals(artifact.getType())) { - _ivyResolver.publish(artifact, src); + _ivyResolver.publish(artifact, src, overwrite); } else { - _artifactResolver.publish(artifact, src); + _artifactResolver.publish(artifact, src, overwrite); } } diff --git a/src/java/fr/jayasoft/ivy/resolver/RepositoryResolver.java b/src/java/fr/jayasoft/ivy/resolver/RepositoryResolver.java index 28d23136..cdb2991b 100644 --- a/src/java/fr/jayasoft/ivy/resolver/RepositoryResolver.java +++ b/src/java/fr/jayasoft/ivy/resolver/RepositoryResolver.java @@ -113,7 +113,7 @@ public class RepositoryResolver extends AbstractResourceResolver { return ivyTempFile.length(); } - public void publish(Artifact artifact, File src) throws IOException { + public void publish(Artifact artifact, File src, boolean overwrite) throws IOException { String destPattern; if ("ivy".equals(artifact.getType()) && !getIvyPatterns().isEmpty()) { destPattern = (String)getIvyPatterns().get(0); @@ -121,7 +121,7 @@ public class RepositoryResolver extends AbstractResourceResolver { destPattern = (String)getArtifactPatterns().get(0); } String dest = IvyPatternHelper.substitute(destPattern, artifact); - _repository.put(src, dest); + _repository.put(src, dest, overwrite); Message.info("\tpublished "+artifact.getName()+" to "+dest); } diff --git a/src/java/fr/jayasoft/ivy/util/FileUtil.java b/src/java/fr/jayasoft/ivy/util/FileUtil.java index ebe3592e..a3e00606 100644 --- a/src/java/fr/jayasoft/ivy/util/FileUtil.java +++ b/src/java/fr/jayasoft/ivy/util/FileUtil.java @@ -25,6 +25,16 @@ public class FileUtil { // 8 * 1024 is also the size used by ant in its FileUtils... maybe they've done more study about it ;-) private static final int BUFFER_SIZE = 8 * 1024; public static void copy(File src, File dest, CopyProgressListener l) throws IOException { + copy(src, dest, l, false); + } + public static void copy(File src, File dest, CopyProgressListener l, boolean overwrite) throws IOException { + if (dest.exists() && !dest.canWrite()) { + if (overwrite && dest.isFile()) { + dest.delete(); + } else { + throw new IOException("impossible to copy: destination is not writable: "+dest); + } + } copy(new FileInputStream(src), dest, l); dest.setLastModified(src.lastModified()); } diff --git a/test/java/fr/jayasoft/ivy/ant/IvyPublishTest.java b/test/java/fr/jayasoft/ivy/ant/IvyPublishTest.java index b17fd84c..fa7b1789 100644 --- a/test/java/fr/jayasoft/ivy/ant/IvyPublishTest.java +++ b/test/java/fr/jayasoft/ivy/ant/IvyPublishTest.java @@ -5,12 +5,15 @@ */ package fr.jayasoft.ivy.ant; +import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import junit.framework.TestCase; import org.apache.tools.ant.Project; import org.apache.tools.ant.taskdefs.Delete; +import org.apache.tools.ant.taskdefs.Echo; import fr.jayasoft.ivy.Ivy; import fr.jayasoft.ivy.ModuleDescriptor; @@ -91,4 +94,82 @@ public class IvyPublishTest extends TestCase { assertEquals("1.2", md.getModuleRevisionId().getRevision()); } + public void testReadonly() throws Exception { + _project.setProperty("ivy.dep.file", "test/java/fr/jayasoft/ivy/ant/ivy-simple.xml"); + IvyResolve res = new IvyResolve(); + res.setProject(_project); + res.execute(); + + _publish.setPubrevision("1.2"); + _publish.setResolver("1"); + File art = new File("build/test/publish/resolve-simple-1.2.jar"); + FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), art, null); + + Echo echo = new Echo(); + echo.setProject(_project); + echo.setMessage("new version"); + echo.setFile(art); + echo.execute(); + + File dest = new File("test/repositories/1/jayasoft/resolve-simple/jars/resolve-simple-1.2.jar"); + FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), + dest, null); + + echo = new Echo(); + echo.setProject(_project); + echo.setMessage("old version"); + echo.setFile(dest); + echo.execute(); + + dest.setReadOnly(); + + try { + _publish.execute(); + fail("by default, publish should fail when a readonly artifact already exist"); + } catch (Exception ex) { + assertTrue(dest.exists()); + BufferedReader reader = new BufferedReader(new FileReader(dest)); + assertEquals("old version", reader.readLine()); + reader.close(); + } + } + + public void testOverwrite() throws Exception { + _project.setProperty("ivy.dep.file", "test/java/fr/jayasoft/ivy/ant/ivy-simple.xml"); + IvyResolve res = new IvyResolve(); + res.setProject(_project); + res.execute(); + + _publish.setPubrevision("1.2"); + _publish.setResolver("1"); + File art = new File("build/test/publish/resolve-simple-1.2.jar"); + FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), art, null); + + Echo echo = new Echo(); + echo.setProject(_project); + echo.setMessage("new version"); + echo.setFile(art); + echo.execute(); + + File dest = new File("test/repositories/1/jayasoft/resolve-simple/jars/resolve-simple-1.2.jar"); + FileUtil.copy(new File("test/repositories/1/org1/mod1.1/jars/mod1.1-1.0.jar"), + dest, null); + + echo = new Echo(); + echo.setProject(_project); + echo.setMessage("old version"); + echo.setFile(dest); + echo.execute(); + + dest.setReadOnly(); + + + _publish.setOverwrite(true); + _publish.execute(); + assertTrue(dest.exists()); + BufferedReader reader = new BufferedReader(new FileReader(dest)); + assertEquals("new version", reader.readLine()); + reader.close(); + } + } diff --git a/test/java/fr/jayasoft/ivy/resolver/FileSystemResolverTest.java b/test/java/fr/jayasoft/ivy/resolver/FileSystemResolverTest.java index 30b53299..597821b7 100644 --- a/test/java/fr/jayasoft/ivy/resolver/FileSystemResolverTest.java +++ b/test/java/fr/jayasoft/ivy/resolver/FileSystemResolverTest.java @@ -342,8 +342,8 @@ public class FileSystemResolverTest extends TestCase { Artifact ivyArtifact = new DefaultArtifact(mrid, new Date(), "ivy", "ivy", "xml"); Artifact artifact = new DefaultArtifact(mrid, new Date(), "myartifact", "mytype", "myext"); File src = new File("test/repositories/ivyconf.xml"); - resolver.publish(ivyArtifact, src); - resolver.publish(artifact, src); + resolver.publish(ivyArtifact, src, false); + resolver.publish(artifact, src, false); assertTrue(new File("test/repositories/1/myorg/mymodule/myrevision/ivy.xml").exists()); assertTrue(new File("test/repositories/1/myorg/mymodule/mytypes/myartifact-myrevision.myext").exists()); diff --git a/test/java/fr/jayasoft/ivy/resolver/MockResolver.java b/test/java/fr/jayasoft/ivy/resolver/MockResolver.java index 50d670ee..f59097e3 100644 --- a/test/java/fr/jayasoft/ivy/resolver/MockResolver.java +++ b/test/java/fr/jayasoft/ivy/resolver/MockResolver.java @@ -66,7 +66,7 @@ public class MockResolver extends AbstractResolver { public DownloadReport download(Artifact[] artifacts, Ivy ivy, File cache) { return null; } - public void publish(Artifact artifact, File src) throws IOException { + public void publish(Artifact artifact, File src, boolean overwrite) throws IOException { } }