You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ivy-commits@incubator.apache.org by xa...@apache.org on 2007/12/01 02:30:54 UTC

svn commit: r600041 - in /incubator/ivy/core/trunk: ./ doc/configuration/ src/java/org/apache/ivy/ src/java/org/apache/ivy/core/event/publish/ src/java/org/apache/ivy/core/publish/ test/java/org/apache/ivy/core/publish/

Author: xavier
Date: Fri Nov 30 18:30:50 2007
New Revision: 600041

URL: http://svn.apache.org/viewvc?rev=600041&view=rev
Log:
IMPROVEMENT: add publish triggers to event system (IVY-650) (thanks to Jason Trump)

Added:
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java   (with props)
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java   (with props)
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java   (with props)
    incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java   (with props)
    incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml   (with props)
    incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml   (with props)
Modified:
    incubator/ivy/core/trunk/CHANGES.txt
    incubator/ivy/core/trunk/doc/configuration/triggers.html
    incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java
    incubator/ivy/core/trunk/src/java/org/apache/ivy/core/publish/PublishEngine.java
    incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEngineTest.java

Modified: incubator/ivy/core/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/CHANGES.txt?rev=600041&r1=600040&r2=600041&view=diff
==============================================================================
--- incubator/ivy/core/trunk/CHANGES.txt (original)
+++ incubator/ivy/core/trunk/CHANGES.txt Fri Nov 30 18:30:50 2007
@@ -49,6 +49,7 @@
 	Andreas Sahlbach
 	John Shields
 	Johan Stuyts
+	Jason Trump
 	Tjeerd Verhagen
 	John Williams
 	Jaroslaw Wypychowski
@@ -74,7 +75,7 @@
 - FIX: Invalid character in IvyRetrieveTest causing most tests to fail (IVY-604)
 - FIX: ivy:settings and ivy:retrieve with explicit id causes unwarranted DEPRECATED warning (thanks to Jacob Grydholt Jensen)
 
-
+- IMPROVEMENT: add publish triggers to event system (IVY-650) (thanks to Jason Trump)
 - IMPROVEMENT: Only display unique circular dependencies during Resolve (IVY-653)
 - IMPROVEMENT: Adding option 'cp', which makes it possible for main to be loaded from file (IVY-543) (thanks to Tjeerd Verhagen)
 - IMPROVEMENT: BasicURLHandler should use method=head for getURLInfo (IVY-611) (thanks to Jim Bonanno)

Modified: incubator/ivy/core/trunk/doc/configuration/triggers.html
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/doc/configuration/triggers.html?rev=600041&r1=600040&r2=600041&view=diff
==============================================================================
--- incubator/ivy/core/trunk/doc/configuration/triggers.html (original)
+++ incubator/ivy/core/trunk/doc/configuration/triggers.html Fri Nov 30 18:30:50 2007
@@ -120,6 +120,41 @@
         </td>
         <td>Fired after an artifact has been downloaded from a repository to the cache</td>
     </tr>
+    <tr><td>pre-publish-artifact <br><span class="since">since 2.0</span></td>
+        <td>
+          <ul>
+            <li>organisation</li>the organisation of the artifact which is about to be published
+            <li>module</li>the name of the module of the artifact which is about to be published
+            <li>revision</li>the revision of the the artifact which is about to be published
+            <li>artifact</li>the name of the the artifact which is about to be published
+            <li>type</li>the type of the the artifact which is about to be published
+            <li>ext</li>the extension of the the artifact which is about to be published
+            <li>resolver</li>the name of the resolver into which the artifact is about to be published
+            <li>file</li>the absolute path of the source file for the artifact
+            <li>overwrite</li>"true" if the new data will overwrite existing artifacts, "false" otherwise
+          </ul>
+        </td>
+        <td>Fired before an artifact is published into a repository</td>
+    </tr>
+    <tr><td>post-publish-artifact <br><span class="since">since 2.0</span></td>
+        <td>
+          <ul>
+            <li>organisation</li>the organisation of the artifact that was published
+            <li>module</li>the name of the module of the artifact that was published
+            <li>revision</li>the revision of the the artifact that was published
+            <li>artifact</li>the name of the the artifact that was published
+            <li>type</li>the type of the the artifact that was published
+            <li>ext</li>the extension of the the artifact that was published
+            <li>resolver</li>the name of the resolver into which the artifact was published
+            <li>file</li>the absolute path of the source file for the artifact
+            <li>overwrite</li>"true" if the new data overwrote existing artifacts, "false" otherwise
+            <li>status</li>"successful" if the artifact published successfully; "failed" if the artifact failed to publish, or if the status is unknown
+          </ul>
+        </td>
+        <td>Fired after an artifact is published into a repository.  Note that this event is fired
+        whether or not the publication succeeded.  The "status" property can be checked to 
+        verify success.</td>
+    </tr>
 </tbody>
 </table>
 

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java?rev=600041&r1=600040&r2=600041&view=diff
==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/Ivy.java Fri Nov 30 18:30:50 2007
@@ -286,7 +286,7 @@
                 deliverEngine = new DeliverEngine(settings);
             }
             if (publishEngine == null) {
-                publishEngine = new PublishEngine(settings);
+                publishEngine = new PublishEngine(settings, eventManager);
             }
             if (installEngine == null) {
                 installEngine = new InstallEngine(
@@ -810,7 +810,7 @@
             DependencyResolver resolver = (DependencyResolver) iter.next();
             if (resolver instanceof BasicResolver) {
                 ((BasicResolver) resolver).setEventManager(eventManager);
-            }
+    }
         }
     }
 

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java Fri Nov 30 18:30:50 2007
@@ -0,0 +1,56 @@
+/*
+ *  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.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Event fired after artifact publication has finished (possibly in error). Triggers registered on
+ * {@link #NAME} will be notified of these events.
+ * 
+ * @see DependencyResolver#publish(Artifact, File, boolean)
+ */
+public class EndArtifactPublishEvent extends PublishEvent {
+
+    private static final long serialVersionUID = -65690169431499422L;
+
+    public static final String NAME = "post-publish-artifact";
+
+    public static final String STATUS_SUCCESSFUL = "successful";
+
+    public static final String STATUS_FAILED = "failed";
+
+    private final boolean successful;
+
+    public EndArtifactPublishEvent(DependencyResolver resolver, Artifact artifact, File data,
+            boolean overwrite, boolean successful) {
+        super(NAME, resolver, artifact, data, overwrite);
+        this.successful = successful;
+        addAttribute("status", isSuccessful() ? STATUS_SUCCESSFUL : STATUS_FAILED);
+    }
+
+    /**
+     * @return true iff no errors were encountered during the publication
+     */
+    public boolean isSuccessful() {
+        return successful;
+    }
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/EndArtifactPublishEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java Fri Nov 30 18:30:50 2007
@@ -0,0 +1,77 @@
+/*
+ *  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.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Base class for events fired during {@link DependencyResolver#publish(Artifact, File, boolean)}.
+ * 
+ * @see StartArtifactPublishEvent
+ * @see EndArtifactPublishEvent
+ */
+public abstract class PublishEvent extends IvyEvent {
+
+    private final DependencyResolver resolver;
+
+    private final Artifact artifact;
+
+    private final File data;
+
+    private final boolean overwrite;
+
+    protected PublishEvent(String name, DependencyResolver resolver, Artifact artifact, File data,
+            boolean overwrite) {
+        super(name);
+        this.resolver = resolver;
+        this.artifact = artifact;
+        this.data = data;
+        this.overwrite = overwrite;
+
+        addMridAttributes(artifact.getModuleRevisionId());
+        addAttributes(artifact.getAttributes());
+        addAttribute("resolver", resolver.getName());
+        addAttribute("file", data.getAbsolutePath());
+        addAttribute("overwrite", String.valueOf(overwrite));
+    }
+
+    /** @return the resolver into which the artifact is being published */
+    public DependencyResolver getResolver() {
+        return resolver;
+    }
+
+    /** @return a local file containing the artifact data */
+    public File getData() {
+        return data;
+    }
+
+    /** @return metadata about the artifact being published */
+    public Artifact getArtifact() {
+        return artifact;
+    }
+
+    /** @return true iff this event overwrites existing resolver data for this artifact */
+    public boolean isOverwrite() {
+        return overwrite;
+    }
+
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/PublishEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java (added)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java Fri Nov 30 18:30:50 2007
@@ -0,0 +1,42 @@
+/*
+ *  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.ivy.core.event.publish;
+
+import java.io.File;
+
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.plugins.resolver.DependencyResolver;
+
+/**
+ * Event fired just before an artifact is published into a resolver. Triggers registered on
+ * {@link #NAME} will be notified of these events.
+ * 
+ * @see DependencyResolver#publish(Artifact, File, boolean)
+ */
+public class StartArtifactPublishEvent extends PublishEvent {
+
+    private static final long serialVersionUID = -1134274781039590219L;
+
+    public static final String NAME = "pre-publish-artifact";
+
+    public StartArtifactPublishEvent(DependencyResolver resolver, Artifact artifact, File data,
+            boolean overwrite) {
+        super(NAME, resolver, artifact, data, overwrite);
+    }
+
+}

Propchange: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/event/publish/StartArtifactPublishEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: incubator/ivy/core/trunk/src/java/org/apache/ivy/core/publish/PublishEngine.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/src/java/org/apache/ivy/core/publish/PublishEngine.java?rev=600041&r1=600040&r2=600041&view=diff
==============================================================================
--- incubator/ivy/core/trunk/src/java/org/apache/ivy/core/publish/PublishEngine.java (original)
+++ incubator/ivy/core/trunk/src/java/org/apache/ivy/core/publish/PublishEngine.java Fri Nov 30 18:30:50 2007
@@ -33,8 +33,10 @@
 
 import org.apache.ivy.core.IvyContext;
 import org.apache.ivy.core.IvyPatternHelper;
-import org.apache.ivy.core.cache.CacheManager;
 import org.apache.ivy.core.cache.ResolutionCacheManager;
+import org.apache.ivy.core.event.EventManager;
+import org.apache.ivy.core.event.publish.EndArtifactPublishEvent;
+import org.apache.ivy.core.event.publish.StartArtifactPublishEvent;
 import org.apache.ivy.core.module.descriptor.Artifact;
 import org.apache.ivy.core.module.descriptor.DefaultArtifact;
 import org.apache.ivy.core.module.descriptor.MDArtifact;
@@ -49,9 +51,11 @@
 
 public class PublishEngine {
     private PublishEngineSettings settings;
+    private EventManager eventManager;
 
-    public PublishEngine(PublishEngineSettings settings) {
+    public PublishEngine(PublishEngineSettings settings, EventManager eventManager) {
         this.settings = settings;
+        this.eventManager = eventManager;
     }
 
     /**
@@ -228,11 +232,21 @@
             DependencyResolver resolver, boolean overwrite) throws IOException {
         IvyContext.getContext().checkInterrupted();
         File src = new File(IvyPatternHelper.substitute(srcArtifactPattern, artifact));
-        if (src.exists()) {
-            resolver.publish(artifact, src, overwrite);
-            return true;
-        } else {
-            return false;
+        
+        //notify triggers that an artifact is about to be published
+        eventManager.fireIvyEvent(
+            new StartArtifactPublishEvent(resolver, artifact, src, overwrite));
+        boolean successful = false; //set to true once the publish succeeds
+        try {
+            if (src.exists()) {
+                resolver.publish(artifact, src, overwrite);
+                successful = true;
+            }
+            return successful;
+        } finally {
+            //notify triggers that the publish is finished, successfully or not.
+            eventManager.fireIvyEvent(
+                new EndArtifactPublishEvent(resolver, artifact, src, overwrite, successful));
         }
     }
 

Modified: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEngineTest.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEngineTest.java?rev=600041&r1=600040&r2=600041&view=diff
==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEngineTest.java (original)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEngineTest.java Fri Nov 30 18:30:50 2007
@@ -50,7 +50,7 @@
 
     public void testAtomicity() throws Exception {
         IvySettings settings = new IvySettings();
-        final PublishEngine engine = new PublishEngine(settings);
+        final PublishEngine engine = new PublishEngine(settings, new EventManager());
         final int[] counter = new int[] {0};
         
         final DefaultModuleDescriptor md = DefaultModuleDescriptor

Added: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java (added)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java Fri Nov 30 18:30:50 2007
@@ -0,0 +1,440 @@
+/*
+ *  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.ivy.core.publish;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URLDecoder;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+
+import org.apache.ivy.Ivy;
+import org.apache.ivy.core.IvyContext;
+import org.apache.ivy.core.event.IvyEvent;
+import org.apache.ivy.core.event.publish.EndArtifactPublishEvent;
+import org.apache.ivy.core.event.publish.PublishEvent;
+import org.apache.ivy.core.event.publish.StartArtifactPublishEvent;
+import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.module.descriptor.MDArtifact;
+import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
+import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
+import org.apache.ivy.plugins.resolver.MockResolver;
+import org.apache.ivy.plugins.trigger.AbstractTrigger;
+
+import junit.framework.TestCase;
+
+public class PublishEventsTest extends TestCase {
+
+    //maps ArtifactRevisionId to PublishTestCase instance.
+    private HashMap expectedPublications;
+    //expected values for the current artifact being published.
+    private PublishTestCase currentTestCase;
+    private boolean expectedOverwrite;
+    
+    //number of times PrePublishTrigger has been invoked successfully
+    private int preTriggers;
+    //number of times PostPublishTrigger has been invoked successfully
+    private int postTriggers;
+    //number of times an artifact has been successfully published by the resolver
+    private int publications;
+
+    //dummy test data that is reused by all cases.
+    private File ivyFile;
+    private Artifact ivyArtifact;
+    private File dataFile;
+    private Artifact dataArtifact;
+    
+    private ModuleDescriptor publishModule;
+    private Collection publishSources;
+    private PublishOptions publishOptions;
+
+    //if non-null, InstrumentedResolver will throw this exception during publish
+    private IOException publishError;
+    
+    //the ivy instance under test
+    private Ivy ivy;
+    private PublishEngine publishEngine;
+
+    protected void setUp() throws Exception {
+        super.setUp();
+        
+        //reset test case state.
+        resetCounters();
+        
+        //this ivy settings should configure an InstrumentedResolver, PrePublishTrigger, and PostPublishTrigger
+        //(see inner classes below).
+        ivy = Ivy.newInstance();
+        ivy.configure(PublishEventsTest.class.getResource("ivysettings-publisheventstest.xml"));
+        ivy.pushContext();
+        publishEngine = ivy.getPublishEngine();
+
+        //setup dummy ivy and data files to test publishing.  since we're testing the engine and not the resolver,
+        //we don't really care whether the file actually gets published.  we just want to make sure
+        //that the engine calls the correct methods in the correct order, and fires required events.
+        String resourcePath = PublishEventsTest.class.getResource("ivy-1.0-dev.xml").getPath();
+        resourcePath = URLDecoder.decode(resourcePath, System.getProperty("file.encoding"));
+        ivyFile = new File(resourcePath);
+        assertTrue("path to ivy file found in test environment", ivyFile.exists());
+        //the contents of the data file don't matter.
+        dataFile = File.createTempFile("ivydata", ".jar");
+        dataFile.deleteOnExit();
+        
+        publishModule = XmlModuleDescriptorParser.getInstance().parseDescriptor(ivy.getSettings(), ivyFile.toURL(), false);
+        //always use the same source data file, no pattern substitution is required.
+        publishSources = Collections.singleton(dataFile.getAbsolutePath());
+        //always use the same ivy file, no pattern substitution is required.
+        publishOptions = new PublishOptions();
+        publishOptions.setSrcIvyPattern(ivyFile.getAbsolutePath());
+        
+        //set up our expectations for the test.  these variables will
+        //be checked by the resolver and triggers during publication.
+        dataArtifact = publishModule.getAllArtifacts()[0];
+        assertEquals("sanity check", "foo", dataArtifact.getName());
+        ivyArtifact = MDArtifact.newIvyArtifact(publishModule);
+
+        expectedPublications = new HashMap();
+        expectedPublications.put(dataArtifact.getId(), new PublishTestCase(dataArtifact, dataFile, true));
+        expectedPublications.put(ivyArtifact.getId(), new PublishTestCase(ivyArtifact, ivyFile, true));
+        assertEquals("hashCode sanity check:  two artifacts expected during publish", 2, expectedPublications.size());
+
+        //push the TestCase instance onto the context stack, so that our
+        //triggers and resolver instances can interact with it it.
+        IvyContext.getContext().push(PublishEventsTest.class.getName(), this);
+    }
+    
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        //reset test state.
+        resetCounters();
+        
+        //test case is finished, pop the test context off the stack.
+        IvyContext.getContext().pop(PublishEventsTest.class.getName());
+        
+        //cleanup ivy resources
+        if (ivy != null) {
+            ivy.popContext();
+            ivy = null;
+        }
+        publishEngine = null;
+        if (dataFile != null)
+            dataFile.delete();
+        dataFile = null;
+        ivyFile = null;
+    }
+
+    protected void resetCounters() {
+        preTriggers = 0;
+        postTriggers = 0;
+        publications = 0;
+        
+        expectedPublications = null;
+        expectedOverwrite = false;
+        publishError = null;
+        currentTestCase = null;
+        
+        ivyArtifact = null;
+        dataArtifact = null;
+    }
+    
+    /**
+     * Test a simple artifact publish, without errors or overwrite settings.
+     */
+    public void testPublishNoOverwrite() throws IOException {
+        //no modifications to input required for this case -- call out to the resolver, and verify that
+        //all of our test counters have been incremented.
+        Collection missing = publishEngine.publish(publishModule.getModuleRevisionId(), publishSources, "default", publishOptions);
+        assertEquals("no missing artifacts", 0, missing.size());
+        
+        //if all tests passed, all of our counter variables should have been updated.
+        assertEquals("pre-publish trigger fired and passed all tests", 2, preTriggers);
+        assertEquals("post-publish trigger fired and passed all tests", 2, postTriggers);
+        assertEquals("resolver received a publish() call, and passed all tests", 2, publications);
+        assertEquals("all expected artifacts have been published", 0, expectedPublications.size());
+    }
+    
+    /**
+     * Test a simple artifact publish, with overwrite set to true.
+     */
+    public void testPublishWithOverwrite() throws IOException {
+        //we expect the overwrite settings to be passed through the event listeners and into the publisher.
+        this.expectedOverwrite = true;
+        
+        //set overwrite to true.  InstrumentedResolver will verify that the correct argument value was provided.
+        publishOptions.setOverwrite(true);
+        Collection missing = publishEngine.publish(publishModule.getModuleRevisionId(), publishSources, "default", publishOptions);
+        assertEquals("no missing artifacts", 0, missing.size());
+        
+        //if all tests passed, all of our counter variables should have been updated.
+        assertEquals("pre-publish trigger fired and passed all tests", 2, preTriggers);
+        assertEquals("post-publish trigger fired and passed all tests", 2, postTriggers);
+        assertEquals("resolver received a publish() call, and passed all tests", 2, publications);
+        assertEquals("all expected artifacts have been published", 0, expectedPublications.size());
+    }
+
+    /**
+     * Test an attempted publish with an invalid data file path.
+     */
+    public void testPublishMissingFile() throws IOException {
+        //delete the datafile.  the publish should fail, but all events should still be fired,
+        //and the ivy artifact should still publish successfully.
+        assertTrue("datafile has been destroyed", dataFile.delete());
+        PublishTestCase dataPublish = (PublishTestCase)expectedPublications.get(dataArtifact.getId());
+        dataPublish.expectedSuccess = false;
+        Collection missing = publishEngine.publish(publishModule.getModuleRevisionId(), publishSources, "default", publishOptions);
+        assertEquals("one missing artifact", 1, missing.size());
+        assertSameArtifact("missing artifact was returned", dataArtifact, (Artifact)missing.iterator().next());
+        
+        //if all tests passed, all of our counter variables should have been updated.
+        assertEquals("pre-publish trigger fired and passed all tests", 2, preTriggers);
+        assertEquals("post-publish trigger fired and passed all tests", 2, postTriggers);
+        assertEquals("only the ivy file published successfully", 1, publications);
+        assertEquals("publish of all expected artifacts has been attempted", 0, expectedPublications.size());
+    }
+
+    /**
+     * Test an attempted publish in which the target resolver throws an IOException.
+     */
+    public void testPublishWithException() {
+        //set an error to be thrown during publication of the data file.
+        this.publishError = new IOException("boom!");
+        //we don't care which artifact is attempted; either will fail with an IOException.
+        for (Iterator it = expectedPublications.values().iterator(); it.hasNext(); )
+            ((PublishTestCase)it.next()).expectedSuccess = false;
+
+        try {
+            publishEngine.publish(publishModule.getModuleRevisionId(), publishSources, "default", publishOptions);
+            fail("if the resolver throws an exception, the engine should too");
+        } catch (IOException expected) {
+            assertSame("exception thrown by the resolver should be propagated by the engine", 
+                       this.publishError, expected);
+        }
+        
+        //the publish engine gives up after the resolver throws an exception on the first artifact,
+        //so only one set of events should have been fired.
+        //note that the initial publish error shouldn't prevent the post-publish trigger from firing.
+        assertEquals("pre-publish trigger fired and passed all tests", 1, preTriggers);
+        assertEquals("post-publish trigger fired and passed all tests", 1, postTriggers);
+        assertEquals("resolver never published successfully", 0, publications);
+        assertEquals("publication aborted after first failure", 1, expectedPublications.size());
+    }
+    
+    /**
+     * Assert that two Artifact instances refer to the same artifact and contain the same metadata.
+     */
+    public static void assertSameArtifact(String message, Artifact expected, Artifact actual) {
+        assertEquals(message + ": name", expected.getName(), actual.getName());
+        assertEquals(message + ": id", expected.getId(), actual.getId());
+        assertEquals(message + ": moduleRevisionId", expected.getModuleRevisionId(), actual.getModuleRevisionId());
+        assertTrue(message + ": configurations", Arrays.equals(expected.getConfigurations(), actual.getConfigurations()));
+        assertEquals(message + ": type", expected.getType(), actual.getType());
+        assertEquals(message + ": ext", expected.getExt(), actual.getExt());
+        assertEquals(message + ": publicationDate", expected.getPublicationDate(), actual.getPublicationDate());
+        assertEquals(message + ": attributes", expected.getAttributes(), actual.getAttributes());
+        assertEquals(message + ": url", expected.getUrl(), actual.getUrl());
+    }
+    
+    public static class PublishTestCase {
+        public Artifact expectedArtifact;
+        public File expectedData;
+        public boolean expectedSuccess;
+        
+        public boolean preTriggerFired;
+        public boolean published;
+        public boolean postTriggerFired;
+        
+        public PublishTestCase(Artifact artifact, File data, boolean success) {
+            this.expectedArtifact = artifact;
+            this.expectedData = data;
+            this.expectedSuccess = success;
+        }
+    }
+    
+    /**
+     * Base class for pre- and post-publish-artifact triggers.  When the trigger receives an event,
+     * the contents of the publish event are examined to make sure they match the variable settings
+     * on the calling {@link PublishEventsTest#currentTestCase} instance.
+     */
+    public static class TestPublishTrigger extends AbstractTrigger {
+        
+        public void progress(IvyEvent event) {
+            PublishEventsTest test = (PublishEventsTest)IvyContext.getContext().peek(PublishEventsTest.class.getName());
+            InstrumentedResolver resolver = (InstrumentedResolver)test.ivy.getSettings().getResolver("default");
+            
+            assertNotNull("instrumented resolver configured", resolver);
+            assertNotNull("got a reference to the current unit test case", test);
+
+            //test the proper sequence of events by comparing the number of pre-events,
+            //post-events, and actual publications.
+            assertTrue("event is of correct base type", 
+                        event instanceof PublishEvent);
+            
+            PublishEvent pubEvent = (PublishEvent)event;
+            Artifact expectedArtifact = test.currentTestCase.expectedArtifact;
+            File expectedData = test.currentTestCase.expectedData;
+            
+            assertSameArtifact("event records correct artifact", 
+                               expectedArtifact, pubEvent.getArtifact());
+            try {
+                assertEquals("event records correct file", 
+                             expectedData.getCanonicalPath(), pubEvent.getData().getCanonicalPath());
+            
+                assertEquals("event records correct overwrite setting", test.expectedOverwrite, pubEvent.isOverwrite());
+                assertSame("event presents correct resolver", resolver, pubEvent.getResolver());
+    
+                String[] attributes = {
+                        "organisation", "module", "revision", "artifact", "type", "ext", "resolver", "overwrite"
+                };
+                String[] values = {
+                        "apache", "PublishEventsTest", "1.0-dev", expectedArtifact.getName(), expectedArtifact.getType(), expectedArtifact.getExt(), "default", String.valueOf(test.expectedOverwrite)
+                };
+                
+                for (int i = 0; i < attributes.length; ++i)
+                    assertEquals("event declares correct value for " + attributes[i],
+                                 values[i], event.getAttributes().get(attributes[i]));
+                //we test file separately, since it is hard to guaranteean exact path match, but we want
+                //to make sure that both paths point to the same canonical location on the filesystem
+                String filePath = event.getAttributes().get("file").toString();
+                assertEquals("event declares correct value for file", 
+                             expectedData.getCanonicalPath(), new File(filePath).getCanonicalPath());
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        }
+        
+    }
+    
+    /**
+     * Extends the tests done by {@link TestPublishTrigger} to check that pre-publish events are
+     * fired before DependencyResolver.publish() is called, and before post-publish events are fired.
+     */
+    public static class PrePublishTrigger extends TestPublishTrigger {
+
+        public void progress(IvyEvent event) {
+            
+            PublishEventsTest test = (PublishEventsTest)IvyContext.getContext().peek(PublishEventsTest.class.getName());
+            assertTrue("event is of correct concrete type", 
+                        event instanceof StartArtifactPublishEvent);
+            StartArtifactPublishEvent startEvent = (StartArtifactPublishEvent)event;
+            
+            //verify that the artifact being publish was in the expected set.  set the 'currentTestCase'
+            //pointer so that the resolver and post-publish trigger can check against it.
+            Artifact artifact = startEvent.getArtifact();
+            assertNotNull("event defines artifact", artifact);
+ 
+            PublishTestCase currentTestCase = (PublishTestCase)test.expectedPublications.remove(artifact.getId());
+            assertNotNull("artifact " + artifact.getId() + " was expected for publication", currentTestCase);
+            assertFalse("current publication has not been visited yet", currentTestCase.preTriggerFired);
+            assertFalse("current publication has not been visited yet", currentTestCase.published);
+            assertFalse("current publication has not been visited yet", currentTestCase.postTriggerFired);
+            test.currentTestCase = currentTestCase;
+            
+            //superclass tests common attributes of publish events
+            super.progress(event);
+            
+            //increment the call counter in the test
+            currentTestCase.preTriggerFired = true;
+            ++test.preTriggers;
+        }
+        
+    }
+    
+    /**
+     * Extends the tests done by {@link TestPublishTrigger} to check that post-publish events are
+     * fired after DependencyResolver.publish() is called, and that the "status" attribute is
+     * set to the correct value.
+     */
+    public static class PostPublishTrigger extends TestPublishTrigger {
+        
+        public void progress(IvyEvent event) {
+            //superclass tests common attributes of publish events
+            super.progress(event);
+            
+            PublishEventsTest test = (PublishEventsTest)IvyContext.getContext().peek(PublishEventsTest.class.getName());
+            
+            //test the proper sequence of events by comparing the current count of pre-events,
+            //post-events, and actual publications.
+            assertTrue("event is of correct concrete type", 
+                        event instanceof EndArtifactPublishEvent);
+            assertTrue("pre-publish event has been triggered", test.preTriggers > 0);
+
+            //test sequence of events
+            assertTrue("pre-trigger event has already been fired for this artifact",
+                       test.currentTestCase.preTriggerFired);
+            assertEquals("publication has been done if possible", 
+                         test.currentTestCase.expectedSuccess, test.currentTestCase.published);
+            assertFalse("post-publish event has not yet been fired for this artifact",
+                        test.currentTestCase.postTriggerFired);
+                        
+            //test the "status" attribute of the post- event.
+            EndArtifactPublishEvent endEvent = (EndArtifactPublishEvent)event;
+            assertEquals("status bit is set correctly", 
+                         test.currentTestCase.expectedSuccess, endEvent.isSuccessful());
+            
+            String expectedStatus = test.currentTestCase.expectedSuccess ? "successful" : "failed";
+            assertEquals("status attribute is set to correct value", 
+                         expectedStatus, endEvent.getAttributes().get("status"));
+            
+            //increment the call counter in the wrapper test
+            test.currentTestCase.postTriggerFired = true;
+            ++test.postTriggers;
+        }
+
+    }
+    
+    /**
+     * When publish() is called, verifies that a pre-publish event has been fired, and also verifies that
+     * the method arguments have the correct value.  Also simulates an IOException if the current
+     * test case demands it.
+     */
+    public static class InstrumentedResolver extends MockResolver {
+
+        public void publish(Artifact artifact, File src, boolean overwrite) throws IOException {
+
+            //verify that the data from the current test case has been handed down to us
+            PublishEventsTest test = (PublishEventsTest)IvyContext.getContext().peek(PublishEventsTest.class.getName());
+
+            //test sequence of events.
+            assertNotNull(test.currentTestCase);
+            assertTrue("preTrigger has already fired", test.currentTestCase.preTriggerFired);
+            assertFalse("postTrigger has not yet fired", test.currentTestCase.postTriggerFired);
+            assertFalse("publish has not been called", test.currentTestCase.published);
+            
+            //test event data
+            assertSameArtifact("publisher has received correct artifact", 
+                                test.currentTestCase.expectedArtifact, artifact);
+            assertEquals("publisher has received correct datafile", 
+                         test.currentTestCase.expectedData.getCanonicalPath(), src.getCanonicalPath());
+            assertEquals("publisher has received correct overwrite setting", 
+                         test.expectedOverwrite, overwrite);
+            assertTrue("publisher only invoked when source file exists", test.currentTestCase.expectedData.exists());
+            
+            //simulate a publisher error if the current test case demands it.
+            if (test.publishError != null)
+                throw test.publishError;
+            
+            //all assertions pass.  increment the publication count
+            test.currentTestCase.published = true;
+            ++test.publications;
+        }
+    }
+    
+}
\ No newline at end of file

Propchange: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/PublishEventsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml (added)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml Fri Nov 30 18:30:50 2007
@@ -0,0 +1,25 @@
+<!--
+   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.    
+-->
+<!-- Dummy module used by PublishEventsTest.java -->
+<ivy-module version="1.0">
+    <info organisation="apache" module="PublishEventsTest" revision="1.0-dev"/>
+	<publications>
+		<artifact name="foo" type="jar"/>
+	</publications>
+</ivy-module>

Propchange: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivy-1.0-dev.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml
URL: http://svn.apache.org/viewvc/incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml?rev=600041&view=auto
==============================================================================
--- incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml (added)
+++ incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml Fri Nov 30 18:30:50 2007
@@ -0,0 +1,36 @@
+<!--
+   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.    
+-->
+<!-- 
+	This ivy configuration is used with org.apache.ivy.core.publish.PublishEventsTest, to unit test basic features
+	of the PublishEngine.
+-->
+<ivysettings>
+	<!-- we define a special resolver that interacts with PublishEventsTest -->
+	<typedef name="publish-events-test" classname="org.apache.ivy.core.publish.PublishEventsTest$InstrumentedResolver"/>
+	<!-- we define triggers to test the behavior of pre- and post-publish events -->
+	<typedef name="pre-publish-test" classname="org.apache.ivy.core.publish.PublishEventsTest$PrePublishTrigger"/>
+	<typedef name="post-publish-test" classname="org.apache.ivy.core.publish.PublishEventsTest$PostPublishTrigger"/>
+	<triggers>
+		<pre-publish-test event="pre-publish-artifact"/>
+		<post-publish-test event="post-publish-artifact"/>
+	</triggers>
+	<resolvers>
+		<publish-events-test name="default"/>
+	</resolvers>
+</ivysettings>

Propchange: incubator/ivy/core/trunk/test/java/org/apache/ivy/core/publish/ivysettings-publisheventstest.xml
------------------------------------------------------------------------------
    svn:eol-style = native