You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@ant.apache.org by xa...@apache.org on 2008/01/14 09:26:49 UTC

svn commit: r611739 - in /ant/ivy/core/trunk: doc/ doc/configuration/caches/ doc/use/ src/java/org/apache/ivy/ant/ src/java/org/apache/ivy/core/cache/ src/java/org/apache/ivy/core/module/id/ src/java/org/apache/ivy/core/settings/ src/java/org/apache/iv...

Author: xavier
Date: Mon Jan 14 00:26:46 2008
New Revision: 611739

URL: http://svn.apache.org/viewvc?rev=611739&view=rev
Log:
add fine grain ttl settings (IVY-694)

Added:
    ant/ivy/core/trunk/doc/configuration/caches/ttl.html   (with props)
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java   (with props)
    ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java   (with props)
Modified:
    ant/ivy/core/trunk/doc/configuration/caches/cache.html
    ant/ivy/core/trunk/doc/toc.json
    ant/ivy/core/trunk/doc/use/resolve.html
    ant/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyResolve.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleId.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/IvySettings.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/ivy.properties
    ant/ivy/core/trunk/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/util/Configurator.java
    ant/ivy/core/trunk/src/java/org/apache/ivy/util/filter/NoFilter.java
    ant/ivy/core/trunk/test/java/org/apache/ivy/core/resolve/ResolveTest.java
    ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/XmlSettingsParserTest.java
    ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/ivysettings-cache.xml
    ant/ivy/core/trunk/test/java/org/apache/ivy/util/ConfiguratorTest.java

Modified: ant/ivy/core/trunk/doc/configuration/caches/cache.html
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/doc/configuration/caches/cache.html?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/doc/configuration/caches/cache.html (original)
+++ ant/ivy/core/trunk/doc/configuration/caches/cache.html Mon Jan 14 00:26:46 2008
@@ -47,10 +47,39 @@
         <td>No, defaults to default cache artifact pattern as configured in [[configuration/caches]]</td></tr>
     <tr><td>lockStrategy</td><td>the name of the [[configuration/lock-strategies lock strategy]] to use for this cache</td>
         <td>No, defaults to default lock strategy as configured in [[configuration/caches]]</td></tr>
+    <tr><td>defaultTTL</td><td>the default [[configuration/caches/ttl TTL]] to use when no specific one is defined</td>
+        <td>No, defaults to ${ivy.cache.ttl.default}</td></tr>
 </tbody>
 </table>
 
-</textarea>
+<h1>Child elements</h1>
+<table class="ivy-children">
+<thead>
+    <tr><th class="ivy-chld">Element</th><th class="ivy-chld-desc">Description</th><th class="ivy-chld-card">Cardinality</th></tr>
+</thead>
+<tbody>
+    <tr><td>[[configuration/caches/ttl]]</td><td>defines a TTL rule</td>
+        <td>0..n</td></tr>
+</tbody>
+</table>
+
+<h1>Examples</h1>
+<code>
+<cache name="mycache" 
+       basedir="/path/to/mycache"
+       ivyPattern="[module]/ivy-[revision].xml" 
+       artifactPattern="[module]/[artifact]-[revision].[ext]"
+       lockStrategy="no-lock"
+       defaultTTL="1s">
+    <ttl revision="latest.integration" duration="200ms" />
+    <ttl organisation="org1" duration="10m 20s" />
+    <ttl organisation="org2" duration="5h" />
+    <ttl organisation="org3" duration="2d 12h" />
+</cache>
+</code>
+Defines a cache called 'mycache', storing files in the '/path/to/mycache' directory using '[module]/ivy-[revision].xml' as pattern to store Ivy files and '[module]/[artifact]-[revision].[ext]' as pattern to store other artifacts.
+The lock strategy used by this cache is the 'no-lock' strategy, which does not perform any locking. 
+The defaultTTL used is of 1s, meaning that by default dynamic revision result will be stored and used for one second. TTL rules then define that all 'latest.integration' revisions will be stored and used for 200ms, while other dynamic revisions from org1 org2 and org3 modules will be stored respectively for 10 minutes 20 seconds; 5 hours; and 2 days and 12 hours.</textarea>
 <script type="text/javascript">xooki.postProcess();</script>
 </body>
 </html>

Added: ant/ivy/core/trunk/doc/configuration/caches/ttl.html
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/doc/configuration/caches/ttl.html?rev=611739&view=auto
==============================================================================
--- ant/ivy/core/trunk/doc/configuration/caches/ttl.html (added)
+++ ant/ivy/core/trunk/doc/configuration/caches/ttl.html Mon Jan 14 00:26:46 2008
@@ -0,0 +1,70 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<!--
+   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.    
+-->
+<html>
+<head>
+	<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
+	<script type="text/javascript">var xookiConfig = {level: 2};</script>	
+	<script type="text/javascript" src="../../xooki/xooki.js"></script>
+</head>
+<body>
+	<textarea id="xooki-source">
+<b>Tag:</b> ttl
+
+<span class="since">Since 2.0</span> Defines a TTL (Time To Live) rule for resolved revision caching.
+
+When Ivy resolves a dynamic version constraint (like latest.integration or a version range), it can store the result of the resolution (like latest.integration=1.5.1) for a given time, called TTL. It means that Ivy will reuse this dynamic revision resolution result without accessing the repositories for the duration of the TTL, unless running [[ant:resolve]] in refresh mode.
+
+This tag let you define a rule to define a TTL specific to a set of dynamic revision, based on the whole module revision information (organization, module name, revision, ...). The revision considered in the rule is the revision before the resolution (for instance 'latest.integration') and not the resolved revision (for instance '1.5.1').
+
+The rules are evaluated in order, the first matching rule being used to define the TTL. If no rule matches, the cache defaultTTL will be used.
+
+The format used to specify the TTL is of the form:
+<code>
+XXd XXh XXm XXs XXXms
+</code>
+Where 'd' stands for days, 'h' for hours, 'm' for minutes, 's' for seconds and 'ms' for milliseconds. Any part of the specification can be omitted, so '12d', '2h 5m' and '1d 5ms' are all valid.
+
+Using a 0ms TTL disable resolved revision caching for the given rule.
+
+<h1>Attributes</h1>
+<table class="ivy-attributes">
+<thead>
+    <tr><th class="ivy-att">Attribute</th><th class="ivy-att-desc">Description</th><th class="ivy-att-req">Required</th></tr>
+</thead>
+<tbody>
+    <tr><td>organisation</td><td>the organisation to match to apply the rule.</td>
+        <td>No, defaults to *</td></tr>
+    <tr><td>module</td><td>the module's name to match to apply the rule.</td>
+        <td>No, defaults to *</td></tr>
+    <tr><td>revision</td><td>the module's revision to match to apply the rule. Note that the version is not resolved when evaluating the rule ('latest.integration' for instance).</td>
+        <td>No, defaults to *</td></tr>
+    <tr><td><em>any extra attribute</em></td><td>an extra attribute to match to apply the rule.</td>
+        <td>No, defaults to *</td></tr>
+    <tr><td>matcher</td><td>the <a href="../../concept.html#matcher">matcher</a> to use to match the modules to which the resolver should be applied</td>
+        <td>No, defaults to exact</td></tr>
+    <tr><td>duration</td><td>the TTL to apply</td>
+        <td>Yes</td></tr>
+</tbody>
+</table>
+
+</textarea>
+<script type="text/javascript">xooki.postProcess();</script>
+</body>
+</html>

Propchange: ant/ivy/core/trunk/doc/configuration/caches/ttl.html
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ant/ivy/core/trunk/doc/toc.json
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/doc/toc.json?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/doc/toc.json (original)
+++ ant/ivy/core/trunk/doc/toc.json Mon Jan 14 00:26:46 2008
@@ -200,7 +200,13 @@
                                 "id":"configuration/caches/cache",
                                 "title":"cache",
                                 "children": [
+                                    {
+                                      "id":"configuration/caches/ttl",
+                                      "title":"ttl",
+                                      "children": [
 
+                                        ]
+                                    }
                                   ]
                               }
                             ]

Modified: ant/ivy/core/trunk/doc/use/resolve.html
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/doc/use/resolve.html?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/doc/use/resolve.html (original)
+++ ant/ivy/core/trunk/doc/use/resolve.html Mon Jan 14 00:26:46 2008
@@ -103,6 +103,8 @@
     <tr><td>useOrigin</td><td>true to avoid the copy of local artifacts to the cache and use directly their original location, false otherwise <span class="since">since 1.4</span>. 
 To know if an artifact is local ivy asks to the resolver. Only filesystem resolver is considered local by default, but this can be disabled if you want to force the copy on one filesystem resolver and use the original location on another. Note that it is safe to use useOrigin even if you some no local resolvers, Ivy will behave as usual in this case. Note also that this only applies to artifacts, not to ivy files, which are still copied in the cache.</td><td>No. defaults to false</td></tr>
 
+    <tr><td>refresh</td><td>true to force Ivy to resolve dynamic revision in this resolve process, false to use cached resolved revision <span class="since">since 2.0</span></td><td>No. defaults to false</td></tr>
+
     <tr><td>inline</td><td>true to use inline mode, false to resolve an ivy file <span class="since">since 1.4</span></td><td>No. defaults to false</td></tr>
 
     <tr><td>organisation</td><td>the organisation of the module to resolve in inline mode <span class="since">since 1.4</span></td><td>Yes in inline mode, no otherwise.</td></tr>

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyResolve.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyResolve.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyResolve.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/ant/IvyResolve.java Mon Jan 14 00:26:46 2008
@@ -59,6 +59,8 @@
 
     private boolean transitive = true;
 
+    private boolean refresh = false;
+
     private boolean changing = false;
 
     private Boolean keep = null;
@@ -140,6 +142,14 @@
     public void setType(String type) {
         this.type = type;
     }
+    
+    public boolean isRefresh() {
+        return refresh;
+    }
+
+    public void setRefresh(boolean refresh) {
+        this.refresh = refresh;
+    }
 
     /**
      * @deprecated Use {@link #setFailureProperty(String)} instead
@@ -298,6 +308,7 @@
                 .setDate(getPubDate(pubdate, null))
                 .setUseCacheOnly(useCacheOnly)
                 .setUseOrigin(useOrigin)
+                .setRefresh(refresh)
                 .setTransitive(transitive)
                 .setResolveId(resolveId);
     }

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/cache/DefaultRepositoryCacheManager.java Mon Jan 14 00:26:46 2008
@@ -23,6 +23,8 @@
 import java.net.URL;
 import java.text.ParseException;
 import java.util.Date;
+import java.util.Map;
+import java.util.regex.Pattern;
 
 import org.apache.ivy.core.IvyPatternHelper;
 import org.apache.ivy.core.module.descriptor.Artifact;
@@ -30,6 +32,7 @@
 import org.apache.ivy.core.module.descriptor.DependencyDescriptor;
 import org.apache.ivy.core.module.descriptor.ModuleDescriptor;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
 import org.apache.ivy.core.report.ArtifactDownloadReport;
 import org.apache.ivy.core.report.DownloadStatus;
 import org.apache.ivy.core.report.MetadataArtifactDownloadReport;
@@ -37,6 +40,8 @@
 import org.apache.ivy.core.settings.IvySettings;
 import org.apache.ivy.plugins.IvySettingsAware;
 import org.apache.ivy.plugins.lock.LockStrategy;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
 import org.apache.ivy.plugins.matcher.Matcher;
 import org.apache.ivy.plugins.matcher.NoMatcher;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
@@ -63,9 +68,6 @@
     private static final String DEFAULT_IVY_PATTERN = 
         "[organisation]/[module]/ivy-[revision].xml";
     
-    // default TTL for resolved revisions is one hour
-    private static final long DEFAULT_TTL = 1000 * 60 * 60 * 1; 
-
     private IvySettings settings;
     
     private File basedir;
@@ -87,8 +89,10 @@
     private String changingMatcherName = PatternMatcher.EXACT_OR_REGEXP;
 
     private Boolean checkmodified;
+    
+    private ModuleRules/*<Long>*/ ttlRules = new ModuleRules();
 
-    private long defaultTTL = DEFAULT_TTL;
+    private Long defaultTTL = null;
 
     public DefaultRepositoryCacheManager() {
     }
@@ -153,11 +157,18 @@
     }
     
     public long getDefaultTTL() {
-        return defaultTTL;
+        if (defaultTTL == null) {
+            defaultTTL = new Long(parseDuration(settings.getVariable("ivy.cache.ttl.default")));
+        }
+        return defaultTTL.longValue();
     }
     
     public void setDefaultTTL(long defaultTTL) {
-        this.defaultTTL = defaultTTL;
+        this.defaultTTL = new Long(defaultTTL);
+    }
+    
+    public void setDefaultTTL(String defaultTTL) {
+        this.defaultTTL = new Long(parseDuration(defaultTTL));
     }
 
     public String getDataFilePattern() {
@@ -196,6 +207,60 @@
         this.changingPattern = changingPattern;
     }
 
+    public void addTTL(Map attributes, PatternMatcher matcher, long duration) {
+        ttlRules.defineRule(new MapMatcher(attributes, matcher), new Long(duration));
+    }
+    
+    public void addConfiguredTtl(Map/*<String,String>*/ attributes) {
+        String duration = (String) attributes.remove("duration");
+        if (duration == null) {
+            throw new IllegalArgumentException("'duration' attribute is mandatory for ttl");
+        }
+        String matcher = (String) attributes.remove("matcher");
+        addTTL(
+            attributes, 
+            matcher == null ? ExactPatternMatcher.INSTANCE : settings.getMatcher(matcher), 
+                    parseDuration(duration));
+    }
+
+
+    private static final Pattern DURATION_PATTERN 
+        = Pattern.compile("(?:(\\d+)d)? ?(?:(\\d+)h)? ?(?:(\\d+)m)? ?(?:(\\d+)s)? ?(?:(\\d+)ms)?");
+
+    private static final int MILLIS_IN_SECONDS = 1000; 
+    private static final int MILLIS_IN_MINUTES = 60 * MILLIS_IN_SECONDS;
+    private static final int MILLIS_IN_HOUR = 60 * MILLIS_IN_MINUTES;
+    private static final int MILLIS_IN_DAY = 24 * MILLIS_IN_HOUR;
+
+    private long parseDuration(String duration) {
+        if (duration == null) {
+            return 0;
+        }
+        java.util.regex.Matcher m = DURATION_PATTERN.matcher(duration);
+        if (m.matches()) {
+            //CheckStyle:MagicNumber| OFF
+            int days = getGroupIntValue(m, 1);
+            int hours = getGroupIntValue(m, 2);
+            int minutes = getGroupIntValue(m, 3);
+            int seconds = getGroupIntValue(m, 4);
+            int millis = getGroupIntValue(m, 5);
+            //CheckStyle:MagicNumber| ON
+            
+            return days * MILLIS_IN_DAY 
+            + hours * MILLIS_IN_HOUR
+            + minutes * MILLIS_IN_MINUTES
+            + seconds * MILLIS_IN_SECONDS
+            + millis;
+        } else {
+            throw new IllegalArgumentException("invalid duration '" 
+                + duration + "': it must match " + DURATION_PATTERN.pattern());
+        }
+    }
+
+    private int getGroupIntValue(java.util.regex.Matcher m, int groupNumber) {
+        String g = m.group(groupNumber);
+        return g == null || g.length() == 0 ? 0 : Integer.parseInt(g);
+    }
 
     /**
      * True if this resolver should check lastmodified date to know if ivy files are up to date.
@@ -508,43 +573,59 @@
     }
 
     private String getResolvedRevision(ModuleRevisionId mrid, CacheMetadataOptions options) {
-        String resolvedRevision = null;
-        if (options.isForce()) {
-            Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
-            return null;
-        }
-        PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
-        String expiration = cachedResolvedRevision.getProperty("expiration.time");
-        if (expiration == null) {
-            Message.verbose("no cached resolved revision for " + mrid);
-            return null;
-        } 
-        if (System.currentTimeMillis() > Long.parseLong(expiration)) {
-            Message.verbose("cached resolved revision expired for " + mrid);
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
             return null;
         }
-        resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
-        if (resolvedRevision == null) {
-            Message.verbose("no cached resolved revision value for " + mrid);
-            return null;
+        try {
+            String resolvedRevision = null;
+            if (options.isForce()) {
+                Message.verbose("refresh mode: no check for cached resolved revision for " + mrid);
+                return null;
+            }
+            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            String expiration = cachedResolvedRevision.getProperty("expiration.time");
+            if (expiration == null) {
+                Message.verbose("no cached resolved revision for " + mrid);
+                return null;
+            } 
+            if (System.currentTimeMillis() > Long.parseLong(expiration)) {
+                Message.verbose("cached resolved revision expired for " + mrid);
+                return null;
+            }
+            resolvedRevision = cachedResolvedRevision.getProperty("resolved.revision");
+            if (resolvedRevision == null) {
+                Message.verbose("no cached resolved revision value for " + mrid);
+                return null;
+            }
+            return resolvedRevision;
+        } finally {
+            unlockMetadataArtifact(mrid);
         }
-        return resolvedRevision;
     }
 
     private void saveResolvedRevision(ModuleRevisionId mrid, String revision) {
-        PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
-        cachedResolvedRevision.setProperty("expiration.time", getExpiration(mrid));
-        cachedResolvedRevision.setProperty("resolved.revision", revision);
-        cachedResolvedRevision.save();
+        if (!lockMetadataArtifact(mrid)) {
+            Message.error("impossible to acquire lock for " + mrid);
+            return;
+        }
+        try {
+            PropertiesFile cachedResolvedRevision = getCachedDataFile(mrid);
+            cachedResolvedRevision.setProperty("expiration.time", getExpiration(mrid));
+            cachedResolvedRevision.setProperty("resolved.revision", revision);
+            cachedResolvedRevision.save();
+        } finally {
+            unlockMetadataArtifact(mrid);
+        }
     }
 
     private String getExpiration(ModuleRevisionId mrid) {
         return String.valueOf(System.currentTimeMillis() + getTTL(mrid));
     }
 
-    protected long getTTL(ModuleRevisionId mrid) {
-        // TODO: implement TTL rules
-        return defaultTTL;
+    public long getTTL(ModuleRevisionId mrid) {
+        Long ttl = (Long) ttlRules.getRule(mrid);
+        return ttl == null ? getDefaultTTL() : ttl.longValue();
     }
 
     public String toString() {
@@ -686,7 +767,8 @@
 
             saveResolvers(md, resolver.getName(), resolver.getName());
             
-            if (getSettings().getVersionMatcher().isDynamic(md.getModuleRevisionId())) {
+            if (getSettings().getVersionMatcher().isDynamic(md.getModuleRevisionId())
+                    && getTTL(md.getModuleRevisionId()) > 0) {
                 saveResolvedRevision(md.getModuleRevisionId(), rmr.getId().getRevision());
             }
                 
@@ -935,4 +1017,5 @@
         Message.debug("\t\tchangingPattern: " + getChangingPattern());
         Message.debug("\t\tchangingMatcher: " + getChangingMatcherName());
     }
+
 }

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleId.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleId.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleId.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleId.java Mon Jan 14 00:26:46 2008
@@ -19,6 +19,8 @@
 
 import java.util.Map;
 import java.util.WeakHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 /**
  *
@@ -156,5 +158,36 @@
             throw new IllegalArgumentException("badly encoded module id: '" + encoded + "'");
         }
         return new ModuleId(parts[0], parts[1]);
+    }
+
+    /**
+     * Pattern to use to matched mid text representation.
+     * @see #parse(String)
+     */
+    public static final Pattern MID_PATTERN = 
+        Pattern.compile(
+            "(" + ModuleRevisionId.STRICT_CHARS_PATTERN + "*)" 
+            + "#(" + ModuleRevisionId.STRICT_CHARS_PATTERN + "+)"); 
+
+    /**
+     * Parses the module id text representation and returns it as a {@link ModuleId} instance.
+     * 
+     * @param mid
+     *            the module id text representation to parse
+     * @return the ModuleId instance corresponding to the representation
+     * @throws IllegalArgumentException
+     *             if the given text representation cannot be parsed
+     */
+    public static ModuleId parse(String mid) {
+        Matcher m = MID_PATTERN.matcher(mid);
+        if (!m.matches()) {
+            throw new IllegalArgumentException(
+                    "module text representation do not match expected pattern."
+                            + " given mid='" + mid + "' expected form=" + MID_PATTERN.pattern());
+        }
+
+        //CheckStyle:MagicNumber| OFF
+        return newInstance(m.group(1), m.group(2));
+        //CheckStyle:MagicNumber| ON
     }
 }

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRevisionId.java Mon Jan 14 00:26:46 2008
@@ -39,7 +39,7 @@
 
     private static final String NULL_ENCODE = "@#:NULL:#@";
     
-    private static final String STRICT_CHARS_PATTERN = "[a-zA-Z0-9\\-/\\._+=]";
+    static final String STRICT_CHARS_PATTERN = "[a-zA-Z0-9\\-/\\._+=]";
     private static final String REV_STRICT_CHARS_PATTERN 
         = "[a-zA-Z0-9\\-/\\._+=,\\[\\]\\{\\}\\(\\):@]";
 
@@ -86,7 +86,7 @@
         if (!m.matches()) {
             throw new IllegalArgumentException(
                     "module revision text representation do not match expected pattern."
-                            + " given mrid='" + mrid + "' expected form=" + MRID_PATTERN);
+                            + " given mrid='" + mrid + "' expected form=" + MRID_PATTERN.pattern());
         }
 
         //CheckStyle:MagicNumber| OFF

Added: ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java?rev=611739&view=auto
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java (added)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java Mon Jan 14 00:26:46 2008
@@ -0,0 +1,153 @@
+/*
+ *  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.module.id;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.util.Checks;
+import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.NoFilter;
+
+/**
+ * A list of module specific rules.
+ * <p>
+ * This class defines a list of module specific rules. For each module only one rule apply,
+ * sometimes none.
+ * </p>
+ * <p>
+ * To know which rule to apply, they are configured using matchers. So you can define a rule
+ * applying to all module from one particular organization, or to all modules with a revisions
+ * matching a pattern, and so on.
+ * </p>
+ * <p>
+ * Rules condition are evaluated in order, so the first matching rule is returned.
+ * </p>
+ * <p>
+ * Rules themselves can be represented by any object, depending on the purpose of the rule (define
+ * which resolver to use, which TTL in cache, ...)
+ * </p>
+ */
+public class ModuleRules {
+    private Map/*<MapMatcher,Object>*/ rules = new LinkedHashMap();
+    
+    /**
+     * Defines a new rule for the given condition.
+     * 
+     * @param condition
+     *            the condition for which the rule should be applied. Must not be <code>null</code>.
+     * @param rule
+     *            the rule to apply. Must not be <code>null</code>.
+     */
+    public void defineRule(MapMatcher condition, Object rule) {
+        Checks.checkNotNull(condition, "condition");
+        Checks.checkNotNull(rule, "rule");
+        
+        rules.put(condition, rule);
+    }
+
+    /**
+     * Returns the rule object matching the given {@link ModuleRevisionId}, or <code>null</code>
+     * if no rule applies.
+     * 
+     * @param mrid
+     *            the {@link ModuleRevisionId} to search the rule for. 
+     *            Must not be <code>null</code>.
+     * @return the rule object matching the given {@link ModuleRevisionId}, or <code>null</code>
+     *         if no rule applies.
+     * @see #getRule(ModuleRevisionId, Filter)
+     */
+    public Object getRule(ModuleRevisionId mrid) {
+        return getRule(mrid, NoFilter.INSTANCE);
+    }
+    
+    /**
+     * Returns the rule object matching the given {@link ModuleId} and accepted by the given
+     * {@link Filter}, or <code>null</code> if no rule applies.
+     * 
+     * @param mrid
+     *            the {@link ModuleRevisionId} to search the rule for. 
+     *            Must not be <code>null</code>.
+     * @param filter
+     *            the filter to use to filter the rule to return. The {@link Filter#accept(Object)}
+     *            method will be called only with rule objects matching the given
+     *            {@link ModuleId}, and the first rule object accepted by the filter will
+     *            be returned. Must not be <code>null</code>.
+     * @return the rule object matching the given {@link ModuleId}, or <code>null</code>
+     *         if no rule applies.
+     * @see #getRule(ModuleRevisionId, Filter)
+     */
+    public Object getRule(ModuleId mid, Filter filter) {
+        Checks.checkNotNull(mid, "mid");
+        return getRule(new ModuleRevisionId(mid, "", ""), filter);
+    }
+    
+    /**
+     * Returns the rule object matching the given {@link ModuleRevisionId} and accepted by the given
+     * {@link Filter}, or <code>null</code> if no rule applies.
+     * 
+     * @param mrid
+     *            the {@link ModuleRevisionId} to search the rule for. 
+     *            Must not be <code>null</code>.
+     * @param filter
+     *            the filter to use to filter the rule to return. The {@link Filter#accept(Object)}
+     *            method will be called only with rule objects matching the given
+     *            {@link ModuleRevisionId}, and the first rule object accepted by the filter will
+     *            be returned. Must not be <code>null</code>.
+     * @return the rule object matching the given {@link ModuleRevisionId}, or <code>null</code>
+     *         if no rule applies.
+     * @see #getRule(ModuleRevisionId)
+     */
+    public Object getRule(ModuleRevisionId mrid, Filter filter) {
+        Checks.checkNotNull(mrid, "mrid");
+        Checks.checkNotNull(filter, "filter");
+        
+        for (Iterator iter = rules.keySet().iterator(); iter.hasNext();) {
+            MapMatcher midm = (MapMatcher) iter.next();
+            if (midm.matches(mrid.getAttributes())) {
+                Object rule = rules.get(midm);
+                if (filter.accept(rule)) {
+                    return rule;
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Dump the list of rules to {@link Message#debug(String)}
+     * 
+     * @param prefix
+     *            the prefix to use for each line dumped
+     */
+    public void dump(String prefix) {
+        if (rules.isEmpty()) {
+            Message.debug(prefix + "NONE");
+        } else {
+            for (Iterator iter = rules.keySet().iterator(); iter.hasNext();) {
+                MapMatcher midm = (MapMatcher) iter.next();
+                Object rule = rules.get(midm);
+                Message.debug(prefix + midm + " -> " + rule);
+            }
+        }
+    }
+
+}

Propchange: ant/ivy/core/trunk/src/java/org/apache/ivy/core/module/id/ModuleRules.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/IvySettings.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/IvySettings.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/IvySettings.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/IvySettings.java Mon Jan 14 00:26:46 2008
@@ -31,7 +31,6 @@
 import java.util.Collection;
 import java.util.HashMap;
 import java.util.Iterator;
-import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Properties;
@@ -49,6 +48,7 @@
 import org.apache.ivy.core.install.InstallEngineSettings;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
+import org.apache.ivy.core.module.id.ModuleRules;
 import org.apache.ivy.core.module.status.StatusManager;
 import org.apache.ivy.core.publish.PublishEngineSettings;
 import org.apache.ivy.core.repository.RepositoryManagementEngineSettings;
@@ -96,6 +96,7 @@
 import org.apache.ivy.plugins.version.VersionMatcher;
 import org.apache.ivy.plugins.version.VersionRangeMatcher;
 import org.apache.ivy.util.Message;
+import org.apache.ivy.util.filter.Filter;
 import org.apache.ivy.util.url.URLHandlerRegistry;
 
 public class IvySettings implements SortEngineSettings, PublishEngineSettings, ParserSettings,
@@ -120,8 +121,7 @@
 
     private boolean checkUpToDate = true;
 
-    // Map (ModuleIdMatcher -> ModuleSettings)
-    private Map moduleSettings = new LinkedHashMap(); 
+    private ModuleRules moduleSettings = new ModuleRules(); 
 
     // Map (String conflictManagerName -> ConflictManager)
     private Map conflictsManager = new HashMap(); 
@@ -504,14 +504,8 @@
             DependencyResolver resolver = (DependencyResolver) iter.next();
             resolver.dumpSettings();
         }
-        if (!moduleSettings.isEmpty()) {
-            Message.debug("\tmodule settings:");
-            for (Iterator iter = moduleSettings.keySet().iterator(); iter.hasNext();) {
-                MapMatcher midm = (MapMatcher) iter.next();
-                ModuleSettings s = (ModuleSettings) moduleSettings.get(midm);
-                Message.debug("\t\t" + midm + " -> " + s);
-            }
-        }
+        Message.debug("\tmodule settings:");
+        moduleSettings.dump("\t\t");
     }
 
     public void loadProperties(URL url) throws IOException {
@@ -695,7 +689,7 @@
     public void addModuleConfiguration(Map attributes, PatternMatcher matcher, String resolverName,
             String branch, String conflictManager) {
         checkResolverName(resolverName);
-        moduleSettings.put(
+        moduleSettings.defineRule(
             new MapMatcher(attributes, matcher), 
             new ModuleSettings(resolverName, branch, conflictManager));
     }
@@ -793,29 +787,21 @@
     }
 
     public String getResolverName(ModuleRevisionId mrid) {
-        for (Iterator iter = moduleSettings.keySet().iterator(); iter.hasNext();) {
-            MapMatcher midm = (MapMatcher) iter.next();
-            if (midm.matches(mrid.getAttributes())) {
-                ModuleSettings ms = (ModuleSettings) moduleSettings.get(midm);
-                if (ms.getResolverName() != null) {
-                    return ms.getResolverName();
-                }
+        ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(mrid, new Filter() {
+            public boolean accept(Object o) {
+                return ((ModuleSettings) o).getResolverName() != null;
             }
-        }
-        return defaultResolverName;
+        });
+        return ms == null ? defaultResolverName : ms.getResolverName();
     }
 
     public String getDefaultBranch(ModuleId moduleId) {
-        for (Iterator iter = moduleSettings.keySet().iterator(); iter.hasNext();) {
-            MapMatcher midm = (MapMatcher) iter.next();
-            if (midm.matches(getAttributes(moduleId))) {
-                ModuleSettings ms = (ModuleSettings) moduleSettings.get(midm);
-                if (ms.getBranch() != null) {
-                    return ms.getBranch();
-                }
+        ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(moduleId, new Filter() {
+            public boolean accept(Object o) {
+                return ((ModuleSettings) o).getBranch() != null;
             }
-        }
-        return getDefaultBranch();
+        });
+        return ms == null ? getDefaultBranch() : ms.getBranch();
     }
 
     public String getDefaultBranch() {
@@ -827,37 +813,22 @@
     }
 
     public ConflictManager getConflictManager(ModuleId moduleId) {
-        for (Iterator iter = moduleSettings.keySet().iterator(); iter.hasNext();) {
-            MapMatcher midm = (MapMatcher) iter.next();
-            if (midm.matches(getAttributes(moduleId))) {
-                ModuleSettings ms = (ModuleSettings) moduleSettings.get(midm);
-                if (ms.getConflictManager() != null) {
-                    ConflictManager cm = getConflictManager(ms.getConflictManager());
-                    if (cm == null) {
-                        throw new IllegalStateException(
-                                "ivy badly configured: unknown conflict manager "
-                                        + ms.getConflictManager());
-                    }
-                    return cm;
-                }
+        ModuleSettings ms = (ModuleSettings) moduleSettings.getRule(moduleId, new Filter() {
+            public boolean accept(Object o) {
+                return ((ModuleSettings) o).getConflictManager() != null;
+            }
+        });
+        if (ms == null) {
+            return getDefaultConflictManager();
+        } else {
+            ConflictManager cm = getConflictManager(ms.getConflictManager());
+            if (cm == null) {
+                throw new IllegalStateException(
+                        "ivy badly configured: unknown conflict manager "
+                                + ms.getConflictManager());
             }
+            return cm;
         }
-        return getDefaultConflictManager();
-    }
-
-    /**
-     * Converts the given module id to a Map containing entries for the organisation and module
-     * name.
-     * 
-     * @param moduleId
-     *            the module id to convert
-     * @return a Map with exactly two entries, one for the organisation, one for the module name.
-     */
-    private Map getAttributes(ModuleId moduleId) {
-        Map att = new HashMap();
-        att.put(IvyPatternHelper.ORGANISATION_KEY, moduleId.getOrganisation());
-        att.put(IvyPatternHelper.MODULE_KEY, moduleId.getName());
-        return att;
     }
 
     public void addConfigured(ConflictManager cm) {

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/ivy.properties
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/ivy.properties?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/ivy.properties (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/core/settings/ivy.properties Mon Jan 14 00:26:46 2008
@@ -32,6 +32,7 @@
 ivy.retrieve.pattern = ${ivy.lib.dir}/[artifact]-[revision].[ext]
 ivy.deliver.ivy.pattern = ${ivy.distrib.dir}/[type]s/[artifact]-[revision].[ext]
 ivy.publish.src.artifacts.pattern = ${ivy.distrib.dir}/[type]s/[artifact]-[revision].[ext]
+ivy.cache.ttl.default = 10s
 
 ivy.report.output.pattern = [organisation]-[module]-[conf].[ext]
 

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/plugins/matcher/MapMatcher.java Mon Jan 14 00:26:46 2008
@@ -34,7 +34,10 @@
         this.pm = pm;
         for (Iterator iter = attributes.entrySet().iterator(); iter.hasNext();) {
             Entry entry = (Entry) iter.next();
-            matchers.put(entry.getKey(), pm.getMatcher((String) entry.getValue()));
+            String value = (String) entry.getValue();
+            if (value != null) {
+                matchers.put(entry.getKey(), pm.getMatcher(value));
+            }
         }
     }
 

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/util/Configurator.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/util/Configurator.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/util/Configurator.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/util/Configurator.java Mon Jan 14 00:26:46 2008
@@ -357,6 +357,11 @@
         }
 
         public void addSetMethod(String name, Method m) {
+            Method current = (Method) setMethods.get(name);
+            if (current != null && current.getParameterTypes()[0] == String.class) {
+                // setter methods with String attribute take precedence 
+                return;
+            }
             setMethods.put(name, m);
         }
 
@@ -493,7 +498,11 @@
                 addChild = parentOD.getAddConfiguredMethod(name);
                 if (addChild != null) {
                     childClass = addChild.getParameterTypes()[0];
-                    child = childClass.newInstance();
+                    if (Map.class == childClass) {
+                        child = new HashMap();
+                    } else {
+                        child = childClass.newInstance();
+                    }
                     setCurrent(child, name);
                     return child;
                 }
@@ -545,7 +554,11 @@
         addChild = parentOD.getAddConfiguredMethod(childClass);
         if (addChild != null) {
             if (child == null) {
-                child = childClass.newInstance();
+                if (Map.class == childClass) {
+                    child = new HashMap();
+                } else {
+                    child = childClass.newInstance();
+                }
             }
             setCurrent(child, name);
             return child;
@@ -577,6 +590,10 @@
         }
         Method m = od.getSetMethod(attributeName);
         if (m == null) {
+            if (od.getObject() instanceof Map) {
+                ((Map) od.getObject()).put(attributeName, value);
+                return;
+            } 
             throw new IllegalArgumentException("no set method found for " + attributeName + " on "
                     + od.getObject().getClass());
         }

Modified: ant/ivy/core/trunk/src/java/org/apache/ivy/util/filter/NoFilter.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/src/java/org/apache/ivy/util/filter/NoFilter.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/src/java/org/apache/ivy/util/filter/NoFilter.java (original)
+++ ant/ivy/core/trunk/src/java/org/apache/ivy/util/filter/NoFilter.java Mon Jan 14 00:26:46 2008
@@ -27,4 +27,7 @@
         return true;
     }
 
+    public String toString() {
+        return "NoFilter";
+    }
 }

Added: ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java?rev=611739&view=auto
==============================================================================
--- ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java (added)
+++ ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java Mon Jan 14 00:26:46 2008
@@ -0,0 +1,150 @@
+/*
+ *  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.module.id;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.ivy.core.IvyPatternHelper;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
+import org.apache.ivy.plugins.matcher.MapMatcher;
+import org.apache.ivy.plugins.matcher.PatternMatcher;
+import org.apache.ivy.util.filter.Filter;
+import org.apache.ivy.util.filter.NoFilter;
+
+import junit.framework.TestCase;
+
+public class ModuleRulesTest extends TestCase {
+	private ModuleRules rules;
+    private Object[] rule;
+
+    protected void setUp() throws Exception {
+        rules = new ModuleRules();
+        rule = new Object[10];
+        for (int i = 0; i < rule.length; i++) {
+            rule[i] = "RULE_" + i;
+        }
+    }
+
+    // tests
+    
+    public void testGetRule() throws Exception {
+        // fixture
+        rules.defineRule(mapMatcher().organization("apache").build(), rule[0]);
+        rules.defineRule(mapMatcher().organization("other").build(), rule[1]);
+        
+        // test
+        assertRule(rule[0], "apache#module1;1.5");
+        assertRule(rule[0], "apache#module2;3.0");
+        assertRule(rule[1], "other#module2;1.5");
+        assertRule(null, "unknown#module1;1.5");
+    }
+
+    public void testGetRuleWithFilter() throws Exception {
+        // fixture
+        rules.defineRule(mapMatcher().organization("apache").build(), rule[0]);
+        rules.defineRule(mapMatcher().module("module1").build(), rule[1]);
+        rules.defineRule(mapMatcher().module("module2").build(), rule[2]);
+        
+        // test
+        assertRule(rule[0], "apache#module1;1.5", acceptAll());
+        assertRule(rule[1], "apache#module1;1.5", acceptSecond());
+        assertModuleIdRule(rule[1], "apache#module1", acceptSecond());
+        assertRule(null, "apache#module1;1.5", acceptNone());
+        assertRule(rule[2], "apache#module2;1.5", acceptSecond());
+        assertRule(null, "unknown#module4;1.5", acceptAll());
+    }
+
+    
+    // test helpers
+    
+    private Filter acceptNone() {
+        return new Filter() {
+            public boolean accept(Object o) {
+                return false;
+            }
+
+            public String toString() {
+                return "AcceptNone";
+            }
+        };
+    }
+
+    private Filter acceptSecond() {
+        return new Filter() {
+            private int cpt;
+
+            public boolean accept(Object o) {
+                return ++cpt == 2;
+            }
+        
+            public String toString() {
+                return "AcceptSecond";
+            }
+        };
+    }
+
+    private Filter acceptAll() {
+        return NoFilter.INSTANCE;
+    }
+
+    private void assertRule(Object rule, String mrid) {
+        Object ruleFound = rules.getRule(ModuleRevisionId.parse(mrid));
+        assertEquals(
+            "unexcepted rule for " + mrid,
+            rule, ruleFound);
+    }
+
+    private void assertRule(Object rule, String mrid, Filter filter) {
+        Object ruleFound = rules.getRule(ModuleRevisionId.parse(mrid), filter);
+        assertEquals(
+            "unexcepted rule for " + mrid + " filtered by " + filter,
+            rule, ruleFound);
+    }
+
+    private void assertModuleIdRule(Object rule, String mid, Filter filter) {
+        Object ruleFound = rules.getRule(ModuleId.parse(mid), filter);
+        assertEquals(
+            "unexcepted rule for " + mid + " filtered by " + filter,
+            rule, ruleFound);
+    }
+
+    private MridMatcherBuilder mapMatcher() {
+        return new MridMatcherBuilder();
+    }
+
+    public class MridMatcherBuilder {
+        private Map attributes = new HashMap();
+        private PatternMatcher matcher = ExactPatternMatcher.INSTANCE;
+        
+        public MridMatcherBuilder organization(String org) {
+            attributes.put(IvyPatternHelper.ORGANISATION_KEY, org);
+            return this;
+        }
+
+        public MridMatcherBuilder module(String mod) {
+            attributes.put(IvyPatternHelper.MODULE_KEY, mod);
+            return this;
+        }
+
+        public MapMatcher build() {
+            return new MapMatcher(attributes, matcher);
+        }
+    }
+
+}

Propchange: ant/ivy/core/trunk/test/java/org/apache/ivy/core/module/id/ModuleRulesTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: ant/ivy/core/trunk/test/java/org/apache/ivy/core/resolve/ResolveTest.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/test/java/org/apache/ivy/core/resolve/ResolveTest.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/test/java/org/apache/ivy/core/resolve/ResolveTest.java (original)
+++ ant/ivy/core/trunk/test/java/org/apache/ivy/core/resolve/ResolveTest.java Mon Jan 14 00:26:46 2008
@@ -22,8 +22,10 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Date;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Map;
 
 import javax.xml.parsers.SAXParser;
 import javax.xml.parsers.SAXParserFactory;
@@ -48,6 +50,7 @@
 import org.apache.ivy.plugins.circular.ErrorCircularDependencyStrategy;
 import org.apache.ivy.plugins.circular.IgnoreCircularDependencyStrategy;
 import org.apache.ivy.plugins.circular.WarnCircularDependencyStrategy;
+import org.apache.ivy.plugins.matcher.ExactPatternMatcher;
 import org.apache.ivy.plugins.parser.xml.XmlModuleDescriptorParser;
 import org.apache.ivy.plugins.resolver.BasicResolver;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
@@ -404,10 +407,11 @@
         assertTrue(getArchiveFileInCache("org1", "mod1.2", "2.0", "mod1.2", "jar", "jar").exists());
     }
 
-    public void testDynamicFromCache1() throws Exception {
+    public void testDynamicFromCache() throws Exception {
         // mod1.4;1.0.2 depends on mod1.2;[1.0,2.0[
 
         Ivy ivy = ivyTestCache();
+        ivy.getSettings().setVariable("ivy.cache.ttl.default", "10s", true);
 
         // set up repository
         FileUtil.forceDelete(new File("build/testCache2"));
@@ -440,10 +444,99 @@
             report.getConfigurationReport("default").getModuleRevisionIds());
     }
 
+    public void testDynamicFromCacheWithTTL0() throws Exception {
+        // mod1.4;1.0.2 depends on mod1.2;[1.0,2.0[
+
+        Ivy ivy = ivyTestCache();
+        ivy.getSettings().setVariable("ivy.cache.ttl.default", "0ms", true);
+
+        // set up repository
+        FileUtil.forceDelete(new File("build/testCache2"));
+        FileUtil.copy(
+            new File("test/repositories/1/org1/mod1.2/jars/mod1.2-2.0.jar"), 
+            new File("build/testCache2/mod1.2-1.5.jar"), null);
+
+        // we first do a simple resolve so that module is in cache
+        ResolveReport report = ivy.resolve(new File(
+                "test/repositories/1/org1/mod1.4/ivys/ivy-1.0.2.xml").toURL(),
+            getResolveOptions(new String[] {"*"}));
+        assertFalse(report.hasError());
+
+        assertEquals(
+            new HashSet(Arrays.asList(new ModuleRevisionId[] {
+                    ModuleRevisionId.newInstance("org1", "mod1.2", "1.5")})), 
+            report.getConfigurationReport("default").getModuleRevisionIds());
+
+        // now we update the repository
+        FileUtil.copy(
+            new File("test/repositories/1/org1/mod1.2/jars/mod1.2-2.0.jar"), 
+            new File("build/testCache2/mod1.2-1.6.jar"), null);
+
+        // now do a new resolve: it should not use cached data
+        report = ivy.resolve(new File("test/repositories/1/org1/mod1.4/ivys/ivy-1.0.2.xml").toURL(),
+            getResolveOptions(new String[] {"*"}));
+        assertFalse(report.hasError());
+        
+        assertEquals(
+            new HashSet(Arrays.asList(new ModuleRevisionId[] {
+                    ModuleRevisionId.newInstance("org1", "mod1.2", "1.6")})), 
+            report.getConfigurationReport("default").getModuleRevisionIds());
+    }
+
+    public void testDynamicFromCacheWithTTL() throws Exception {
+        // mod1.4;1.0.2 depends on mod1.2;[1.0,2.0[
+        Ivy ivy = ivyTestCache();
+        ivy.getSettings().setVariable("ivy.cache.ttl.default", "10s", true);
+        ((DefaultRepositoryCacheManager) ivy.getSettings().getDefaultRepositoryCacheManager())
+            .addTTL(ModuleRevisionId.newInstance("org1", "*", "*").getAttributes(), 
+                ExactPatternMatcher.INSTANCE, 500);
+
+        // set up repository
+        FileUtil.forceDelete(new File("build/testCache2"));
+        FileUtil.copy(
+            new File("test/repositories/1/org1/mod1.2/jars/mod1.2-2.0.jar"), 
+            new File("build/testCache2/mod1.2-1.5.jar"), null);
+
+        // we first do a simple resolve so that module is in cache
+        ResolveReport report = ivy.resolve(new File(
+                "test/repositories/1/org1/mod1.4/ivys/ivy-1.0.2.xml").toURL(),
+            getResolveOptions(new String[] {"*"}));
+        assertFalse(report.hasError());
+
+        // now we update the repository
+        FileUtil.copy(
+            new File("test/repositories/1/org1/mod1.2/jars/mod1.2-2.0.jar"), 
+            new File("build/testCache2/mod1.2-1.6.jar"), null);
+
+        // now do a new resolve: it should use cached data
+        report = ivy.resolve(new File("test/repositories/1/org1/mod1.4/ivys/ivy-1.0.2.xml").toURL(),
+            getResolveOptions(new String[] {"*"}));
+        assertFalse(report.hasError());
+        
+        assertEquals(
+            new HashSet(Arrays.asList(new ModuleRevisionId[] {
+                    ModuleRevisionId.newInstance("org1", "mod1.2", "1.5")})), 
+            report.getConfigurationReport("default").getModuleRevisionIds());
+        
+        // wait for org1 TTL to expire
+        Thread.sleep(700);
+
+        // now do a new resolve: it should resolve the dynamic revision again
+        report = ivy.resolve(new File("test/repositories/1/org1/mod1.4/ivys/ivy-1.0.2.xml").toURL(),
+            getResolveOptions(new String[] {"*"}));
+        assertFalse(report.hasError());
+        
+        assertEquals(
+            new HashSet(Arrays.asList(new ModuleRevisionId[] {
+                    ModuleRevisionId.newInstance("org1", "mod1.2", "1.6")})), 
+            report.getConfigurationReport("default").getModuleRevisionIds());
+    }
+
 
     public void testRefreshDynamicFromCache() throws Exception {
         // mod1.4;1.0.2 depends on mod1.2;[1.0,2.0[
         Ivy ivy = ivyTestCache();
+        ivy.getSettings().setVariable("ivy.cache.ttl.default", "10s", true);
 
         // set up repository
         FileUtil.forceDelete(new File("build/testCache2"));

Modified: ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/XmlSettingsParserTest.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/XmlSettingsParserTest.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/XmlSettingsParserTest.java (original)
+++ ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/XmlSettingsParserTest.java Mon Jan 14 00:26:46 2008
@@ -179,6 +179,14 @@
         DefaultRepositoryCacheManager c = (DefaultRepositoryCacheManager) settings.getRepositoryCacheManager("mycache");
         assertNotNull(c);
         assertEquals("mycache", c.getName());
+        assertEquals(1000, c.getDefaultTTL());
+        assertEquals(200, c.getTTL(ModuleRevisionId.newInstance("apache", "ivy", "latest.integration")));
+        assertEquals(10 * 60 * 1000 + 20 * 1000, // 10m 20s 
+            c.getTTL(ModuleRevisionId.newInstance("org1", "A", "A")));
+        assertEquals(5 * 3600 * 1000, // 5h 
+            c.getTTL(ModuleRevisionId.newInstance("org2", "A", "A")));
+        assertEquals(60 * 3600 * 1000, // 2d 12h = 60h 
+            c.getTTL(ModuleRevisionId.newInstance("org3", "A", "A")));
         assertEquals(new File("mycache"), c.getBasedir());
         assertEquals("no-lock", c.getLockStrategy().getName());
 

Modified: ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/ivysettings-cache.xml
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/ivysettings-cache.xml?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/ivysettings-cache.xml (original)
+++ ant/ivy/core/trunk/test/java/org/apache/ivy/core/settings/ivysettings-cache.xml Mon Jan 14 00:26:46 2008
@@ -28,7 +28,12 @@
 				ivyPattern="[module]/ivy-[revision].xml" 
 				artifactPattern="[module]/[artifact]-[revision].[ext]"
 				lockStrategy="no-lock"
-		/>
+				defaultTTL="1s">
+			<ttl revision="latest.integration" duration="200ms" />
+			<ttl organisation="org1" duration="10m 20s" />
+			<ttl organisation="org2" duration="5h" />
+			<ttl organisation="org3" duration="2d 12h" />
+		</cache>
 		<cache name="mycache2" />
 	</caches>
 	<resolvers>

Modified: ant/ivy/core/trunk/test/java/org/apache/ivy/util/ConfiguratorTest.java
URL: http://svn.apache.org/viewvc/ant/ivy/core/trunk/test/java/org/apache/ivy/util/ConfiguratorTest.java?rev=611739&r1=611738&r2=611739&view=diff
==============================================================================
--- ant/ivy/core/trunk/test/java/org/apache/ivy/util/ConfiguratorTest.java (original)
+++ ant/ivy/core/trunk/test/java/org/apache/ivy/util/ConfiguratorTest.java Mon Jan 14 00:26:46 2008
@@ -19,6 +19,7 @@
 
 import java.util.ArrayList;
 import java.util.List;
+import java.util.Map;
 
 import junit.framework.TestCase;
 
@@ -62,6 +63,8 @@
         private Class _clazz;
 
         private List _trees = new ArrayList();
+        
+        private List _walkers = new ArrayList();
 
         public List getTrees() {
             return _trees;
@@ -70,6 +73,14 @@
         public void addConfiguredTree(Tree tree) {
             _trees.add(tree);
         }
+        
+        public List getWalkers() {
+            return _walkers;
+        }
+        
+        public void addConfiguredWalker(Map walkerAttributes) {
+            _walkers.add(new Person((String) walkerAttributes.get("name")));
+        }
 
         public Class getClazz() {
             return _clazz;
@@ -240,6 +251,18 @@
         _conf.endCreateChild();
         assertEquals(1, street.getTrees().size());
         assertEquals(400, ((Tree) street.getTrees().get(0)).getAge());
+        assertEquals(street, _conf.getCurrent());
+    }
+
+    public void testAddConfiguredWalker() {
+        Street street = new Street();
+        _conf.setRoot(street);
+        _conf.startCreateChild("walker");
+        assertTrue(street.getWalkers().isEmpty());
+        _conf.setAttribute("name", "xavier");
+        _conf.endCreateChild();
+        assertEquals(1, street.getWalkers().size());
+        assertEquals("xavier", ((Person) street.getWalkers().get(0)).getName());
         assertEquals(street, _conf.getCurrent());
     }