IVY-735 Support timeouts on resolvers

This closes #54 pull request at github/apache/ant-ivy


Project: http://git-wip-us.apache.org/repos/asf/ant-ivy/repo
Commit: http://git-wip-us.apache.org/repos/asf/ant-ivy/commit/6607bdcb
Tree: http://git-wip-us.apache.org/repos/asf/ant-ivy/tree/6607bdcb
Diff: http://git-wip-us.apache.org/repos/asf/ant-ivy/diff/6607bdcb

Branch: refs/heads/master
Commit: 6607bdcb3ba69d2ab4f41be6292cc5a6df6365ba
Parents: 6920717
Author: Jaikiran Pai <jaiki...@apache.org>
Authored: Mon Jul 10 10:20:38 2017 +0530
Committer: Jaikiran Pai <jaiki...@apache.org>
Committed: Mon Jul 24 11:07:38 2017 +0530

----------------------------------------------------------------------
 asciidoc/settings/resolvers.adoc                |  10 +-
 asciidoc/settings/timeout-constraint.adoc       |  61 ++++++
 asciidoc/settings/timeout-constraints.adoc      |  54 +++++
 asciidoc/toc.json                               |  13 ++
 .../cache/DefaultResolutionCacheManager.java    |   6 +
 .../ivy/core/cache/ParserSettingsMonitor.java   |   6 +
 .../apache/ivy/core/settings/IvySettings.java   |  57 ++++--
 .../core/settings/NamedTimeoutConstraint.java   |  80 ++++++++
 .../ivy/core/settings/TimeoutConstraint.java    |  46 +++++
 .../ivy/core/settings/XmlSettingsParser.java    |   5 +-
 .../apache/ivy/core/settings/typedef.properties |   3 +
 .../org/apache/ivy/osgi/obr/OBRResolver.java    |   2 +-
 .../ivy/osgi/repo/AbstractOSGiResolver.java     |   2 +-
 .../ivy/osgi/repo/RelativeURLRepository.java    |  18 +-
 .../ivy/osgi/updatesite/UpdateSiteLoader.java   |  19 +-
 .../ivy/osgi/updatesite/UpdateSiteResolver.java |   4 +-
 .../ivy/plugins/parser/ParserSettings.java      |  14 +-
 .../plugins/repository/AbstractRepository.java  |  15 ++
 .../plugins/repository/jar/JarRepository.java   |   9 +
 .../plugins/repository/sftp/SFTPRepository.java |   5 +
 .../ssh/AbstractSshBasedRepository.java         |   5 +
 .../plugins/repository/ssh/SshRepository.java   |   9 +
 .../plugins/repository/url/URLRepository.java   |  14 +-
 .../ivy/plugins/repository/url/URLResource.java |  13 +-
 .../plugins/repository/vfs/VfsRepository.java   |   5 +
 .../repository/vsftp/VsftpRepository.java       |   9 +
 .../ivy/plugins/resolver/AbstractResolver.java  |  29 +++
 .../ivy/plugins/resolver/BasicResolver.java     |   2 +-
 .../ivy/plugins/resolver/JarResolver.java       |   4 +-
 .../plugins/resolver/LazyTimeoutConstraint.java |  47 +++++
 .../plugins/resolver/MirroredURLResolver.java   |   6 +-
 .../ivy/plugins/resolver/SFTPResolver.java      |   2 +-
 .../ivy/plugins/resolver/SshResolver.java       |   2 +-
 .../ivy/plugins/resolver/URLResolver.java       |   3 +-
 .../ivy/plugins/resolver/VfsResolver.java       |   2 +-
 .../ivy/plugins/resolver/VsftpResolver.java     |   2 +-
 src/java/org/apache/ivy/util/FileUtil.java      |  11 +-
 src/java/org/apache/ivy/util/StringUtils.java   |  14 ++
 .../apache/ivy/util/url/AbstractURLHandler.java |  59 ++++--
 .../apache/ivy/util/url/BasicURLHandler.java    | 121 +++++++----
 .../apache/ivy/util/url/HttpClientHandler.java  | 126 ++++++++----
 .../org/apache/ivy/util/url/URLHandler.java     | 201 +++++++++++++++----
 .../ivy/util/url/URLHandlerDispatcher.java      | 110 +++++++---
 .../org/apache/ivy/LocalFileRepoOverHttp.java   |  91 +++++++++
 test/java/org/apache/ivy/TestHelper.java        |  58 ++++--
 .../apache/ivy/core/resolve/ResolveTest.java    |   2 +-
 .../core/settings/XmlSettingsParserTest.java    | 158 +++++++++++----
 .../ivysettings-timeout-constraints.xml         |  30 +++
 .../osgi/updatesite/UpdateSiteLoaderTest.java   |   2 +-
 .../xml/XmlModuleDescriptorParserTest.java      |   2 +-
 .../ivy/plugins/resolver/URLResolverTest.java   | 146 +++++++++++++-
 .../ivy/util/url/AbstractURLHandlerTest.java    |  21 ++
 .../ivy/util/url/BasicURLHandlerTest.java       |   2 +-
 53 files changed, 1454 insertions(+), 283 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/asciidoc/settings/resolvers.adoc
----------------------------------------------------------------------
diff --git a/asciidoc/settings/resolvers.adoc b/asciidoc/settings/resolvers.adoc
index b1fe1aa..14703dd 100644
--- a/asciidoc/settings/resolvers.adoc
+++ b/asciidoc/settings/resolvers.adoc
@@ -104,6 +104,13 @@ Any standard resolver can be used in `force` mode, which 
is used mainly to handl
 By using such a resolver at the beginning of a chain, you can be sure that Ivy 
will pick up whatever module is available in this resolver (usually a private 
local build) instead of the real requested revision. This allows to handle use 
case like a developer working on modules `A` and `C`, where `A -> B -> C`, and 
pick up the local build for `C` without having to publish a local version of 
`B`.
 *__since 2.0__*
 
+==== timeoutConstraint
+
+*__since 2.5__*
+
+All standard resolvers support the `timeoutConstraint` attribute. The value 
for this attribute is the name of the 
link:timeout-constraint.html[timeout-constraint] that's been defined in the Ivy 
settings.
+
+Resolvers can be optionally configured to use a `timeoutConstraint` so that 
the timeouts defined on that constraint dictate how the resolvers behave when 
it comes to dealing with timeouts while establishing connections and reading 
content, during module descriptor and artifact resolutions.
 
 ==== Maven
 
@@ -130,12 +137,13 @@ And setting the property `ivy.maven.lookup.javadoc` to 
`false` disable the looku
 |namespace|The name of the namespace to which this resolver belongs *__since 
1.3__*|No, defaults to 'system'|Yes|Yes
 |checkconsistency|true to check consistency of module descriptors found by 
this resolver, false to avoid consistency check *__since 1.3__*|No, defaults to 
true|No|Yes
 |descriptor|'optional' if a module descriptor (usually an ivy file) is 
optional for this resolver, 'required' to refuse modules without module 
descriptor *__since 2.0__*|No, defaults to 'optional'|No (except dual)|Yes
-|allownomd|_DEPRECATED. Use descriptor="required | optional" instead._
+|allownomd|_DEPRECATED. Use descriptor="required \| optional" instead._
     true if the absence of module descriptor (usually an ivy file) is 
authorised for this resolver, false to refuse modules without module descriptor 
*__since 1.4__*|No, defaults to true|No (except dual)|Yes
 |checksums|a comma separated list of link:../concept.html#checksum[checksum 
algorithms] to use both for publication and checking *__since 1.4__*|No, 
defaults to ${ivy.checksums}|No|Yes
 |latest|The name of the latest strategy to use.|No, defaults to 
'default'|Yes|Yes
 |cache|The name of the cache manager to use.|No, defaults to the value of the 
default attribute of caches|No|Yes
 |signer|The name of the link:../settings/signers.html[detached signature 
generator] to use when publishing artifacts. *__(since 2.2)__*|No, by default 
published artifacts will not get signed by Ivy.|No|Yes
+|timeoutConstraint|The name of the 
link:timeout-constraint.html[timeout-constraint] to use for the resolver. 
*__(since 2.5)__*|No. In the absence of a `timeoutConstraint`, the resolver's 
behaviour with timeouts is implementation specific.|No|Yes
 |=======
 
 

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/asciidoc/settings/timeout-constraint.adoc
----------------------------------------------------------------------
diff --git a/asciidoc/settings/timeout-constraint.adoc 
b/asciidoc/settings/timeout-constraint.adoc
new file mode 100644
index 0000000..ace99ea
--- /dev/null
+++ b/asciidoc/settings/timeout-constraint.adoc
@@ -0,0 +1,61 @@
+////
+   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.
+////
+
+= timeout-constraint
+
+*Tag:* timeout-constraint
+
+Defines a named timeout constraint that can then be referenced from other 
places of the Ivy settings file, like the link:resolvers.html[resolvers].
+
+== Attributes
+
+
+[options="header",cols="15%,50%,35%"]
+|=======
+|Attribute|Description|Required
+|name|name of timeout constraint|Yes
+|connectionTimeout|An integer value, in milli seconds, that will be used as 
the timeout while establishing a connection. +
+A value greater than `0` is used literally as the timeout. +
+A value of `0` indicates no timeout and typically translates to wait-forever 
kind of semantics. +
+A value lesser than `0` lets the users of this timeout constraint decide what 
semantics to use. That effectively, implies implementation specific 
semantics|No, defaults to `-1`
+|readTimeout|An integer value, in milli seconds, that will be used as the 
timeout while reading content from a resource to which an connection has been 
established. +
+A value greater than `0` is used literally as the timeout. +
+A value of `0` indicates no timeout and typically translates to wait-forever 
kind of semantics. +
+A value lesser than `0` lets the users of this timeout constraint decide what 
semantics to use. That effectively, implies implementation specific 
semantics|No, defaults to `-1`
+|=======
+
+== Examples
+
+[source, xml]
+----
+    <timeout-constraints>
+        <timeout-constraint name="test-timeout-1" connectionTimeout="100" 
readTimeout="500"/>
+        <timeout-constraint name="test-timeout-2" readTimeout="20"/>
+        <timeout-constraint name="test-timeout-3" connectionTimeout="400"/>
+        <timeout-constraint name="test-timeout-4"/>
+    </timeout-constraints>
+----
+Here we see 4 timeout constraints defined:
+
+    - `test-timeout-1` uses a connection timeout of 200 milli seconds and read 
timeout of 500 milli seconds.
+    - `test-timeout-2` uses a read timeout of 20 milli seconds and lets the 
connection timeout default to -1.
+    - `test-timeout-3` uses a connection timeout of 400 milli seconds and lets 
the read timeout default to -1.
+    - `test-timeout-4` lets both the connection timeout and read timeout 
default to -1.
+
+

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/asciidoc/settings/timeout-constraints.adoc
----------------------------------------------------------------------
diff --git a/asciidoc/settings/timeout-constraints.adoc 
b/asciidoc/settings/timeout-constraints.adoc
new file mode 100644
index 0000000..e331500
--- /dev/null
+++ b/asciidoc/settings/timeout-constraints.adoc
@@ -0,0 +1,54 @@
+////
+   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.
+////
+
+= timeout-constraints
+
+*__since 2.5__*
+
+Ivy, internally, communicates with various remote systems for dealing with 
module descriptors and artifacts. This is done through various 
link:../concept.html[dependency resolvers] that are configured in the Ivy 
settings. This communication typically involves opening a connection to the 
target system and reading content from those systems. As with all remote 
communication, certain systems can sometimes be slow or even unavailable on 
some occasions. `timeout-constraints` in Ivy settings allows you to configure 
timeouts that can then be used by Ivy while communicating with remote systems.
+
+NOTE: Although, timeouts are most likely to be used by dependency resolvers, 
the setting up of timeouts through the use of `timeout-constraints` doesn't 
really bother about where those timeouts are used. As such, it's _not_ an error 
to have `timeout-constraints` within a Ivy settings file which may never be 
referred to by any resolver.
+
+== Child elements
+
+
+[options="header"]
+|=======
+|Element|Description|Cardinality
+|link:../settings/timeout-constraint.html[timeout-constraint]|defines a new 
timeout-constraint|0..n
+|=======
+
+
+== Examples
+
+
+[source, xml]
+----
+
+<timeout-constraints>
+        <timeout-constraint name="test-timeout-1" connectionTimeout="100" 
readTimeout="500"/>
+        <timeout-constraint name="test-timeout-2" readTimeout="20"/>
+        <timeout-constraint name="test-timeout-3" connectionTimeout="400"/>
+        <timeout-constraint name="test-timeout-4"/>
+</timeout-constraints>
+
+----
+
+Defines 4 `timeout-constraint`, each with a name and values for 
`connectionTimeout` and `readTimeout`. More details about the 
`timeout-constraint` element is explained in 
link:../settings/timeout-constraint.html[its documentation].
+

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/asciidoc/toc.json
----------------------------------------------------------------------
diff --git a/asciidoc/toc.json b/asciidoc/toc.json
index 63408ce..20615dd 100644
--- a/asciidoc/toc.json
+++ b/asciidoc/toc.json
@@ -479,6 +479,19 @@
                           "children": [
 
                             ]
+                        },
+                        {
+                          "id":"settings/timeout-constraints",
+                          "title":"timeout-constraints",
+                          "children": [
+                              {
+                                "id":"settings/timeout-constraint",
+                                "title":"timeout-constraint",
+                                "children": [
+
+                                  ]
+                              }
+                            ]
                         }
                       ]
                   },

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java 
b/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
index 2d08c44..21e0858 100644
--- a/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
+++ b/src/java/org/apache/ivy/core/cache/DefaultResolutionCacheManager.java
@@ -36,6 +36,7 @@ import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.core.module.status.StatusManager;
 import org.apache.ivy.core.settings.IvySettings;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.IvySettingsAware;
 import org.apache.ivy.plugins.conflict.ConflictManager;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
@@ -283,6 +284,11 @@ public class DefaultResolutionCacheManager implements 
ResolutionCacheManager, Iv
         public String getVariable(String value) {
             return delegate.getVariable(value);
         }
+
+        @Override
+        public TimeoutConstraint getTimeoutConstraint(final String name) {
+            return this.delegate.getTimeoutConstraint(name);
+        }
     }
 
     private static final class MapURLResolver extends RelativeUrlResolver {

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java 
b/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
index f383578..426ca27 100644
--- a/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
+++ b/src/java/org/apache/ivy/core/cache/ParserSettingsMonitor.java
@@ -26,6 +26,7 @@ import org.apache.ivy.core.RelativeUrlResolver;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.conflict.ConflictManager;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
 import org.apache.ivy.plugins.namespace.Namespace;
@@ -148,5 +149,10 @@ class ParserSettingsMonitor {
         public String getVariable(String value) {
             return delegatedSettings.getVariable(value);
         }
+
+        @Override
+        public TimeoutConstraint getTimeoutConstraint(final String name) {
+            return delegatedSettings.getTimeoutConstraint(name);
+        }
     };
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/settings/IvySettings.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/settings/IvySettings.java 
b/src/java/org/apache/ivy/core/settings/IvySettings.java
index adaa188..8e9af4b 100644
--- a/src/java/org/apache/ivy/core/settings/IvySettings.java
+++ b/src/java/org/apache/ivy/core/settings/IvySettings.java
@@ -17,26 +17,6 @@
  */
 package org.apache.ivy.core.settings;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
-import java.lang.reflect.Field;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.net.URLClassLoader;
-import java.security.AccessControlException;
-import java.text.ParseException;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Properties;
-
 import org.apache.ivy.Ivy;
 import org.apache.ivy.core.IvyPatternHelper;
 import org.apache.ivy.core.NormalRelativeUrlResolver;
@@ -110,9 +90,30 @@ import org.apache.ivy.plugins.version.VersionRangeMatcher;
 import org.apache.ivy.util.Checks;
 import org.apache.ivy.util.FileUtil;
 import org.apache.ivy.util.Message;
+import org.apache.ivy.util.StringUtils;
 import org.apache.ivy.util.filter.Filter;
 import org.apache.ivy.util.url.URLHandlerRegistry;
 
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Field;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.security.AccessControlException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Properties;
+
 public class IvySettings implements SortEngineSettings, PublishEngineSettings, 
ParserSettings,
         DeliverEngineSettings, CheckEngineSettings, InstallEngineSettings, 
ResolverSettings,
         ResolveEngineSettings, RetrieveEngineSettings, 
RepositoryManagementEngineSettings {
@@ -212,6 +213,8 @@ public class IvySettings implements SortEngineSettings, 
PublishEngineSettings, P
 
     private AbstractWorkspaceResolver workspaceResolver;
 
+    private final Map<String, TimeoutConstraint> timeoutConstraints = new 
HashMap<>();
+
     public IvySettings() {
         this(new IvyVariableContainerImpl());
     }
@@ -1074,6 +1077,20 @@ public class IvySettings implements SortEngineSettings, 
PublishEngineSettings, P
         namespaces.put(ns.getName(), ns);
     }
 
+    public void addConfigured(final NamedTimeoutConstraint timeoutConstraint) {
+        if (timeoutConstraint == null) {
+            return;
+        }
+        final String name = timeoutConstraint.getName();
+        StringUtils.assertNotNullNotEmpty(name, "Name of a timeout constraint 
cannot be null or empty string");
+        this.timeoutConstraints.put(name, timeoutConstraint);
+    }
+
+    @Override
+    public TimeoutConstraint getTimeoutConstraint(final String name) {
+        return this.timeoutConstraints.get(name);
+    }
+
     public synchronized void addConfigured(PatternMatcher m) {
         addMatcher(m);
     }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/settings/NamedTimeoutConstraint.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/settings/NamedTimeoutConstraint.java 
b/src/java/org/apache/ivy/core/settings/NamedTimeoutConstraint.java
new file mode 100644
index 0000000..40cdad5
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/NamedTimeoutConstraint.java
@@ -0,0 +1,80 @@
+/*
+ * 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.settings;
+
+import org.apache.ivy.util.StringUtils;
+
+/**
+ * An implementation of {@link TimeoutConstraint} which can be identified by a 
name
+ */
+public class NamedTimeoutConstraint implements TimeoutConstraint {
+
+
+    private String name;
+
+    private int connectionTimeout = -1;
+
+    private int readTimeout = -1;
+
+    public NamedTimeoutConstraint() {
+
+    }
+
+    public NamedTimeoutConstraint(final String name) {
+        StringUtils.assertNotNullNotEmpty(name, "Name of a timeout constraint 
cannot be null or empty string");
+        this.name = name;
+    }
+
+    public void setName(final String name) {
+        StringUtils.assertNotNullNotEmpty(name, "Name of a timeout constraint 
cannot be null or empty string");
+        this.name = name;
+    }
+
+    /**
+     * @return Returns the name of the timeout constraint
+     */
+    public String getName() {
+        return this.name;
+    }
+
+    @Override
+    public int getConnectionTimeout() {
+        return this.connectionTimeout;
+    }
+
+    @Override
+    public int getReadTimeout() {
+        return this.readTimeout;
+    }
+
+    /**
+     * Sets the connection timeout of this timeout constraint
+     * @param connectionTimeout The connection timeout in milli seconds.
+     */
+    public void setConnectionTimeout(final int connectionTimeout) {
+        this.connectionTimeout = connectionTimeout;
+    }
+
+    /**
+     * Sets the read timeout of this timeout constraint
+     * @param readTimeout The read timeout in milli seconds.
+     */
+    public void setReadTimeout(final int readTimeout) {
+        this.readTimeout = readTimeout;
+    }
+}

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/settings/TimeoutConstraint.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/settings/TimeoutConstraint.java 
b/src/java/org/apache/ivy/core/settings/TimeoutConstraint.java
new file mode 100644
index 0000000..fecf99b
--- /dev/null
+++ b/src/java/org/apache/ivy/core/settings/TimeoutConstraint.java
@@ -0,0 +1,46 @@
+/*
+ * 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.settings;
+
+/**
+ * Represents the timeouts that are applicable while dealing with resources.
+ * <p>
+ * An example of its usage is {@link 
org.apache.ivy.plugins.resolver.DependencyResolver dependency resolvers}
+ * when they are resolving module descriptor and/or are downloading the 
artifacts.
+ */
+public interface TimeoutConstraint {
+
+    /**
+     * @return Returns the timeout, in milli seconds, that's to be used while 
establishing a connection to a resource.
+     * A value greater than zero indicates the specific timeout to be used. A 
value of 0 indicates no timeout
+     * and essentially translates to wait-forever semantics. A value lesser 
than 0 lets the users of this {@link TimeoutConstraint}
+     * decide what kind of timeout semantics to use while establishing a 
connection (for example, some implementations
+     * can decide to use some default value).
+     */
+    int getConnectionTimeout();
+
+    /**
+     * @return Returns the timeout, in milli seconds, that's to be used while 
reading content from a resource.
+     * A value greater than zero indicates the specific timeout to be used. A 
value of 0 indicates no timeout
+     * and essentially translates to wait-forever semantics. A value lesser 
than 0 lets the users of this {@link TimeoutConstraint}
+     * decide what kind of timeout semantics to use reading from the resource 
(for example, some implementations
+     * can decide to use some default value).
+     */
+    int getReadTimeout();
+
+}

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java 
b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
index f245b49..e749337 100644
--- a/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
+++ b/src/java/org/apache/ivy/core/settings/XmlSettingsParser.java
@@ -105,7 +105,7 @@ public class XmlSettingsParser extends DefaultHandler {
 
     private List<String> configuratorTags = Arrays.asList("resolvers", 
"namespaces", "parsers",
         "latest-strategies", "conflict-managers", "outputters", 
"version-matchers", "statuses",
-        "circular-dependency-strategies", "triggers", "lock-strategies", 
"caches", "signers");
+        "circular-dependency-strategies", "triggers", "lock-strategies", 
"caches", "signers", "timeout-constraints");
 
     private IvySettings ivy;
 
@@ -180,6 +180,7 @@ public class XmlSettingsParser extends DefaultHandler {
         doParse(configuration);
     }
 
+    @Override
     public void startElement(String uri, String localName, String qName, 
Attributes att)
             throws SAXException {
         // we first copy attributes in a Map to be able to modify them
@@ -596,6 +597,7 @@ public class XmlSettingsParser extends DefaultHandler {
         }
     }
 
+    @Override
     public void endElement(String uri, String localName, String qName) throws 
SAXException {
         if (configurator.getCurrent() != null) {
             if (configuratorTags.contains(qName) && configurator.getDepth() == 
1) {
@@ -610,6 +612,7 @@ public class XmlSettingsParser extends DefaultHandler {
         }
     }
 
+    @Override
     public void endDocument() throws SAXException {
         if (defaultResolver != null) {
             ivy.setDefaultResolver(ivy.substitute(defaultResolver));

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/core/settings/typedef.properties
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/core/settings/typedef.properties 
b/src/java/org/apache/ivy/core/settings/typedef.properties
index ba140d2..4e3eb8e 100644
--- a/src/java/org/apache/ivy/core/settings/typedef.properties
+++ b/src/java/org/apache/ivy/core/settings/typedef.properties
@@ -65,3 +65,6 @@ cache                 = 
org.apache.ivy.core.cache.DefaultRepositoryCacheManager
 pgp             = 
org.apache.ivy.plugins.signer.bouncycastle.OpenPGPSignatureGenerator
 
 osgi-manifest-parser = org.apache.ivy.osgi.core.OSGiManifestParser
+
+timeout-constraint = org.apache.ivy.core.settings.NamedTimeoutConstraint
+

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/osgi/obr/OBRResolver.java 
b/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
index c1e3bb5..477f258 100644
--- a/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
+++ b/src/java/org/apache/ivy/osgi/obr/OBRResolver.java
@@ -85,7 +85,7 @@ public class OBRResolver extends AbstractOSGiResolver {
                 if (eventManager != null) {
                     getRepository().addTransferListener(eventManager);
                 }
-                Resource obrResource = new URLResource(url);
+                final Resource obrResource = new URLResource(url, 
this.getTimeoutConstraint());
                 CacheResourceOptions options = new CacheResourceOptions();
                 if (metadataTtl != null) {
                     options.setTtl(metadataTtl);

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java 
b/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
index 08e762c..f469264 100644
--- a/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
+++ b/src/java/org/apache/ivy/osgi/repo/AbstractOSGiResolver.java
@@ -296,7 +296,7 @@ public abstract class AbstractOSGiResolver extends 
BasicResolver {
         }
         Message.verbose("\tusing url for " + artifact + ": " + url);
         logArtifactAttempt(artifact, url.toExternalForm());
-        Resource resource = new URLResource(url);
+        final Resource resource = new URLResource(url, 
this.getTimeoutConstraint());
         return new ResolvedResource(resource, 
artifact.getModuleRevisionId().getRevision());
     }
 

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java 
b/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
index 03b6860..8ab90cb 100644
--- a/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
+++ b/src/java/org/apache/ivy/osgi/repo/RelativeURLRepository.java
@@ -24,6 +24,7 @@ import java.net.URL;
 import java.util.HashMap;
 import java.util.Map;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.plugins.repository.url.URLRepository;
 import org.apache.ivy.plugins.repository.url.URLResource;
@@ -37,11 +38,22 @@ public class RelativeURLRepository extends URLRepository {
         baseUrl = null;
     }
 
+    /**
+     *
+     * @param baseUrl
+     * @deprecated Since 2.5. Use {@link #RelativeURLRepository(URL, 
TimeoutConstraint)} instead
+     */
+    @Deprecated
     public RelativeURLRepository(URL baseUrl) {
-        super();
+        this(baseUrl, null);
+    }
+
+    public RelativeURLRepository(final URL baseUrl, final TimeoutConstraint 
timeoutConstraint) {
+        super(timeoutConstraint);
         this.baseUrl = baseUrl;
     }
 
+
     private Map<String, Resource> resourcesCache = new HashMap<>();
 
     public Resource getResource(String source) throws IOException {
@@ -56,9 +68,9 @@ public class RelativeURLRepository extends URLRepository {
                 uri = null;
             }
             if (uri == null || uri.isAbsolute()) {
-                res = new URLResource(new URL(source));
+                res = new URLResource(new URL(source), getTimeoutConstraint());
             } else {
-                res = new URLResource(new URL(baseUrl + source));
+                res = new URLResource(new URL(baseUrl + source), 
getTimeoutConstraint());
             }
             resourcesCache.put(source, res);
         }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java 
b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
index dd0c227..c88be4d 100644
--- a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
+++ b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteLoader.java
@@ -32,6 +32,7 @@ import org.apache.ivy.core.cache.RepositoryCacheManager;
 import org.apache.ivy.core.event.EventManager;
 import org.apache.ivy.core.report.ArtifactDownloadReport;
 import org.apache.ivy.core.report.DownloadStatus;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.osgi.core.ExecutionEnvironmentProfileProvider;
 import org.apache.ivy.osgi.p2.P2ArtifactParser;
 import org.apache.ivy.osgi.p2.P2CompositeParser;
@@ -57,12 +58,16 @@ public class UpdateSiteLoader {
 
     private final CacheResourceOptions options;
 
+    private final TimeoutConstraint timeoutConstraint;
+
     private int logLevel = Message.MSG_INFO;
 
-    public UpdateSiteLoader(RepositoryCacheManager repositoryCacheManager,
-            EventManager eventManager, CacheResourceOptions options) {
+    public UpdateSiteLoader(final RepositoryCacheManager 
repositoryCacheManager,
+                            final EventManager eventManager, final 
CacheResourceOptions options,
+                            final TimeoutConstraint timeoutConstraint) {
         this.repositoryCacheManager = repositoryCacheManager;
         this.options = options;
+        this.timeoutConstraint = timeoutConstraint;
         if (eventManager != null) {
             urlRepository.addTransferListener(eventManager);
         }
@@ -177,7 +182,7 @@ public class UpdateSiteLoader {
         InputStream readIn = null; // the input stream from which the xml 
should be read
 
         URL contentUrl = repoUri.resolve(baseName + ".jar").toURL();
-        URLResource res = new URLResource(contentUrl);
+        URLResource res = new URLResource(contentUrl, this.timeoutConstraint);
 
         ArtifactDownloadReport report = 
repositoryCacheManager.downloadRepositoryResource(res,
             baseName, baseName, "jar", options, urlRepository);
@@ -185,7 +190,7 @@ public class UpdateSiteLoader {
         if (report.getDownloadStatus() == DownloadStatus.FAILED) {
             // no jar file, try the xml one
             contentUrl = repoUri.resolve(baseName + ".xml").toURL();
-            res = new URLResource(contentUrl);
+            res = new URLResource(contentUrl, this.timeoutConstraint);
 
             report = repositoryCacheManager.downloadRepositoryResource(res, 
baseName, baseName,
                 "xml", options, urlRepository);
@@ -226,7 +231,7 @@ public class UpdateSiteLoader {
         URI siteUri = normalizeSiteUri(repoUri, null);
         URL u = siteUri.resolve("site.xml").toURL();
 
-        URLResource res = new URLResource(u);
+        final URLResource res = new URLResource(u, this.timeoutConstraint);
         ArtifactDownloadReport report = 
repositoryCacheManager.downloadRepositoryResource(res,
             "site", "updatesite", "xml", options, urlRepository);
         if (report.getDownloadStatus() == DownloadStatus.FAILED) {
@@ -272,7 +277,7 @@ public class UpdateSiteLoader {
         URL digest = digestBaseUri.resolve("digest.zip").toURL();
         Message.verbose("\tReading " + digest);
 
-        URLResource res = new URLResource(digest);
+        final URLResource res = new URLResource(digest, 
this.timeoutConstraint);
         ArtifactDownloadReport report = 
repositoryCacheManager.downloadRepositoryResource(res,
             "digest", "digest", "zip", options, urlRepository);
         if (report.getDownloadStatus() == DownloadStatus.FAILED) {
@@ -294,7 +299,7 @@ public class UpdateSiteLoader {
         for (EclipseFeature feature : site.getFeatures()) {
             URL url = site.getUri().resolve(feature.getUrl()).toURL();
 
-            URLResource res = new URLResource(url);
+            final URLResource res = new URLResource(url, 
this.timeoutConstraint);
             ArtifactDownloadReport report = 
repositoryCacheManager.downloadRepositoryResource(res,
                 feature.getId(), "feature", "jar", options, urlRepository);
             if (report.getDownloadStatus() == DownloadStatus.FAILED) {

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java 
b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
index f6dde81..8e3d653 100644
--- a/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
+++ b/src/java/org/apache/ivy/osgi/updatesite/UpdateSiteResolver.java
@@ -115,8 +115,8 @@ public class UpdateSiteResolver extends 
AbstractOSGiResolver {
                 }
             }
         });
-        UpdateSiteLoader loader = new 
UpdateSiteLoader(getRepositoryCacheManager(),
-                getEventManager(), options);
+        final UpdateSiteLoader loader = new 
UpdateSiteLoader(getRepositoryCacheManager(),
+                getEventManager(), options, this.getTimeoutConstraint());
         loader.setLogLevel(log);
         RepoDescriptor repoDescriptor;
         try {

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/parser/ParserSettings.java 
b/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
index 8294547..49e5333 100644
--- a/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
+++ b/src/java/org/apache/ivy/plugins/parser/ParserSettings.java
@@ -17,19 +17,20 @@
  */
 package org.apache.ivy.plugins.parser;
 
-import java.io.File;
-import java.util.Map;
-
 import org.apache.ivy.core.RelativeUrlResolver;
 import org.apache.ivy.core.cache.ResolutionCacheManager;
 import org.apache.ivy.core.module.id.ModuleId;
 import org.apache.ivy.core.module.id.ModuleRevisionId;
 import org.apache.ivy.core.module.status.StatusManager;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.conflict.ConflictManager;
 import org.apache.ivy.plugins.matcher.PatternMatcher;
 import org.apache.ivy.plugins.namespace.Namespace;
 import org.apache.ivy.plugins.resolver.DependencyResolver;
 
+import java.io.File;
+import java.util.Map;
+
 public interface ParserSettings {
 
     String substitute(String value);
@@ -60,4 +61,11 @@ public interface ParserSettings {
     Namespace getContextNamespace();
 
     String getVariable(String string);
+
+    /**
+     * @param name The name of the {@link TimeoutConstraint}
+     * @return Returns a {@link TimeoutConstraint} which is identified by the 
passed <code>name</code>. Returns null
+     * if no such {@link TimeoutConstraint} exists
+     */
+    TimeoutConstraint getTimeoutConstraint(String name);
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java 
b/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
index f5419bc..9149050 100644
--- a/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/AbstractRepository.java
@@ -24,6 +24,7 @@ import java.util.Arrays;
 import javax.swing.event.EventListenerList;
 
 import org.apache.ivy.core.module.descriptor.Artifact;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 
 public abstract class AbstractRepository implements Repository {
     private EventListenerList listeners = new EventListenerList();
@@ -32,6 +33,16 @@ public abstract class AbstractRepository implements 
Repository {
 
     private TransferEvent evt;
 
+    private final TimeoutConstraint timeoutConstraint;
+
+    public AbstractRepository() {
+        this(null);
+    }
+
+    protected AbstractRepository(final TimeoutConstraint timeoutConstraint) {
+        this.timeoutConstraint = timeoutConstraint;
+    }
+
     public void addTransferListener(TransferListener listener) {
         listeners.add(TransferListener.class, listener);
     }
@@ -121,6 +132,10 @@ public abstract class AbstractRepository implements 
Repository {
         this.name = name;
     }
 
+    public TimeoutConstraint getTimeoutConstraint() {
+        return this.timeoutConstraint;
+    }
+
     @Override
     public String toString() {
         return getName();

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java 
b/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
index b3ea9be..937c3d4 100644
--- a/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/jar/JarRepository.java
@@ -27,6 +27,7 @@ import java.util.jar.JarEntry;
 import java.util.jar.JarFile;
 import java.util.zip.ZipEntry;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.AbstractRepository;
 import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
 import org.apache.ivy.plugins.repository.Resource;
@@ -39,6 +40,14 @@ public class JarRepository extends AbstractRepository {
 
     private JarFile jarFile;
 
+    public JarRepository() {
+
+    }
+
+    public JarRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     public void setJarFile(JarFile jarFile) {
         this.jarFile = jarFile;
     }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java 
b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
index e1d013c..b18e212 100644
--- a/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/sftp/SFTPRepository.java
@@ -26,6 +26,7 @@ import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.BasicResource;
 import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.plugins.repository.TransferEvent;
@@ -72,6 +73,10 @@ public class SFTPRepository extends 
AbstractSshBasedRepository {
     public SFTPRepository() {
     }
 
+    public SFTPRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     public Resource getResource(String source) {
         return new SFTPResource(this, source);
     }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
 
b/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
index 3411bd6..7bacf8c 100644
--- 
a/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
+++ 
b/src/java/org/apache/ivy/plugins/repository/ssh/AbstractSshBasedRepository.java
@@ -25,6 +25,7 @@ import java.util.HashMap;
 import java.util.Locale;
 import java.util.Map;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.AbstractRepository;
 import org.apache.ivy.util.Credentials;
 import org.apache.ivy.util.CredentialsUtil;
@@ -59,6 +60,10 @@ public abstract class AbstractSshBasedRepository extends 
AbstractRepository {
         super();
     }
 
+    public AbstractSshBasedRepository(final TimeoutConstraint 
timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     /**
      * hashmap of user/hosts with credentials. key is hostname, value is 
Credentials
      **/

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java 
b/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
index 46b380c..a839724 100644
--- a/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/ssh/SshRepository.java
@@ -29,6 +29,7 @@ import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.List;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.util.Message;
 
@@ -57,6 +58,14 @@ public class SshRepository extends 
AbstractSshBasedRepository {
 
     private String publishPermissions = null;
 
+    public SshRepository() {
+
+    }
+
+    public SshRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     /**
      * create a new resource with lazy initializing
      *

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java 
b/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
index d045c60..c2173f0 100644
--- a/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/url/URLRepository.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.AbstractRepository;
 import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
 import org.apache.ivy.plugins.repository.Resource;
@@ -40,10 +41,17 @@ public class URLRepository extends AbstractRepository {
 
     private final Map<String, Resource> resourcesCache = new HashMap<>();
 
+    public URLRepository() {
+    }
+
+    public URLRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     public Resource getResource(String source) throws IOException {
         Resource res = resourcesCache.get(source);
         if (res == null) {
-            res = new URLResource(new URL(source));
+            res = new URLResource(new URL(source), 
this.getTimeoutConstraint());
             resourcesCache.put(source, res);
         }
         return res;
@@ -57,7 +65,7 @@ public class URLRepository extends AbstractRepository {
             if (totalLength > 0) {
                 progress.setTotalLength(totalLength);
             }
-            FileUtil.copy(new URL(source), destination, progress);
+            FileUtil.copy(new URL(source), destination, progress, 
getTimeoutConstraint());
         } catch (IOException | RuntimeException ex) {
             fireTransferError(ex);
             throw ex;
@@ -77,7 +85,7 @@ public class URLRepository extends AbstractRepository {
             if (totalLength > 0) {
                 progress.setTotalLength(totalLength);
             }
-            FileUtil.copy(source, new URL(destination), progress);
+            FileUtil.copy(source, new URL(destination), progress, 
getTimeoutConstraint());
         } catch (IOException | RuntimeException ex) {
             fireTransferError(ex);
             throw ex;

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/url/URLResource.java 
b/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
index c687308..6a1fb3e 100644
--- a/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
+++ b/src/java/org/apache/ivy/plugins/repository/url/URLResource.java
@@ -24,13 +24,15 @@ import java.net.MalformedURLException;
 import java.net.URISyntaxException;
 import java.net.URL;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.LocalizableResource;
 import org.apache.ivy.plugins.repository.Resource;
 import org.apache.ivy.util.url.URLHandler.URLInfo;
 import org.apache.ivy.util.url.URLHandlerRegistry;
 
 public class URLResource implements LocalizableResource {
-    private URL url;
+    private final URL url;
+    private final TimeoutConstraint timeoutConstraint;
 
     private boolean init = false;
 
@@ -40,8 +42,13 @@ public class URLResource implements LocalizableResource {
 
     private boolean exists;
 
-    public URLResource(URL url) {
+    public URLResource(final URL url) {
+        this(url, null);
+    }
+
+    public URLResource(final URL url, final TimeoutConstraint 
timeoutConstraint) {
         this.url = url;
+        this.timeoutConstraint = timeoutConstraint;
     }
 
     public String getName() {
@@ -65,7 +72,7 @@ public class URLResource implements LocalizableResource {
     }
 
     private void init() {
-        URLInfo info = URLHandlerRegistry.getDefault().getURLInfo(url);
+        final URLInfo info = URLHandlerRegistry.getDefault().getURLInfo(url, 
this.timeoutConstraint);
         contentLength = info.getContentLength();
         lastModified = info.getLastModified();
         exists = info.isReachable();

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java 
b/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
index 4706cb6..4eacea6 100644
--- a/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/vfs/VfsRepository.java
@@ -31,6 +31,7 @@ import org.apache.commons.vfs.FileSystemException;
 import org.apache.commons.vfs.FileSystemManager;
 import org.apache.commons.vfs.FileType;
 import org.apache.commons.vfs.impl.StandardFileSystemManager;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.AbstractRepository;
 import org.apache.ivy.plugins.repository.RepositoryCopyProgressListener;
 import org.apache.ivy.plugins.repository.Resource;
@@ -58,6 +59,10 @@ public class VfsRepository extends AbstractRepository {
     public VfsRepository() {
     }
 
+    public VfsRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     private FileSystemManager getVFSManager() throws IOException {
         synchronized (this) {
             if (manager == null) {

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java 
b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
index 463facc..f776143 100644
--- a/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
+++ b/src/java/org/apache/ivy/plugins/repository/vsftp/VsftpRepository.java
@@ -34,6 +34,7 @@ import org.apache.ivy.core.IvyThread;
 import org.apache.ivy.core.event.IvyEvent;
 import org.apache.ivy.core.event.IvyListener;
 import org.apache.ivy.core.event.resolve.EndResolveEvent;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.plugins.repository.AbstractRepository;
 import org.apache.ivy.plugins.repository.BasicResource;
 import org.apache.ivy.plugins.repository.Resource;
@@ -119,6 +120,14 @@ public class VsftpRepository extends AbstractRepository {
 
     private Ivy ivy = null;
 
+    public VsftpRepository() {
+
+    }
+
+    public VsftpRepository(final TimeoutConstraint timeoutConstraint) {
+        super(timeoutConstraint);
+    }
+
     public Resource getResource(String source) throws IOException {
         initIvy();
         return new VsftpResource(this, source);

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
index 59148de..7366bfd 100644
--- a/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/AbstractResolver.java
@@ -54,6 +54,7 @@ import org.apache.ivy.core.resolve.ResolvedModuleRevision;
 import org.apache.ivy.core.search.ModuleEntry;
 import org.apache.ivy.core.search.OrganisationEntry;
 import org.apache.ivy.core.search.RevisionEntry;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.core.settings.Validatable;
 import org.apache.ivy.plugins.conflict.ConflictManager;
 import org.apache.ivy.plugins.latest.ArtifactInfo;
@@ -111,6 +112,9 @@ public abstract class AbstractResolver implements 
DependencyResolver, HasLatestS
 
     private Boolean checkmodified;
 
+    private String timeoutConstraintName;
+    private TimeoutConstraint timeoutConstraint;
+
     public ResolverSettings getSettings() {
         return settings;
     }
@@ -205,6 +209,14 @@ public abstract class AbstractResolver implements 
DependencyResolver, HasLatestS
         return getClass().getName();
     }
 
+    public TimeoutConstraint getTimeoutConstraint() {
+        return this.timeoutConstraint;
+    }
+
+    public void setTimeoutConstraint(final String name) {
+        this.timeoutConstraintName = name;
+    }
+
     /**
      * Default implementation downloads the artifact without taking advantage 
of its location
      *
@@ -412,6 +424,17 @@ public abstract class AbstractResolver implements 
DependencyResolver, HasLatestS
         }
     }
 
+    private void initTimeoutConstraintFromSettings() {
+        if (this.timeoutConstraintName == null) {
+            return;
+        }
+        this.timeoutConstraint = 
settings.getTimeoutConstraint(this.timeoutConstraintName);
+        if (this.timeoutConstraint == null) {
+            throw new IllegalStateException("Unknown timeout constraint '" + 
this.timeoutConstraintName + "' " +
+                    "on resolver '" + this.name + "'");
+        }
+    }
+
     public void setRepositoryCacheManager(RepositoryCacheManager 
repositoryCacheManager) {
         this.cacheManagerName = repositoryCacheManager.getName();
         this.repositoryCacheManager = repositoryCacheManager;
@@ -429,10 +452,12 @@ public abstract class AbstractResolver implements 
DependencyResolver, HasLatestS
         return eventManager;
     }
 
+    @Override
     public void validate() {
         initRepositoryCacheManagerFromSettings();
         initNamespaceFromSettings();
         initLatestStrategyFromSettings();
+        initTimeoutConstraintFromSettings();
     }
 
     protected CacheMetadataOptions getCacheOptions(ResolveData data) {
@@ -617,5 +642,9 @@ public abstract class AbstractResolver implements 
DependencyResolver, HasLatestS
             return AbstractResolver.this.getSettings().getVariable(value);
         }
 
+        @Override
+        public TimeoutConstraint getTimeoutConstraint(final String name) {
+            return 
AbstractResolver.this.getSettings().getTimeoutConstraint(name);
+        }
     }
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
index 780cc05..e759764 100644
--- a/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/BasicResolver.java
@@ -1037,7 +1037,7 @@ public abstract class BasicResolver extends 
AbstractResolver {
                     }
                     resource = new FileResource(new FileRepository(), f);
                 } else {
-                    resource = new URLResource(url);
+                    resource = new URLResource(url, 
this.getTimeoutConstraint());
                 }
                 ret = new ResolvedResource(resource, 
artifact.getModuleRevisionId().getRevision());
             }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/JarResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
index 623a5f5..b02830b 100644
--- a/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/JarResolver.java
@@ -37,7 +37,7 @@ public class JarResolver extends RepositoryResolver {
     private URL url;
 
     public JarResolver() {
-        setRepository(new JarRepository());
+        setRepository(new JarRepository(new LazyTimeoutConstraint(this)));
     }
 
     @Override
@@ -84,7 +84,7 @@ public class JarResolver extends RepositoryResolver {
                 if (eventManager != null) {
                     getRepository().addTransferListener(eventManager);
                 }
-                Resource jarResource = new URLResource(url);
+                Resource jarResource = new URLResource(url, 
this.getTimeoutConstraint());
                 CacheResourceOptions options = new CacheResourceOptions();
                 report = 
getRepositoryCacheManager().downloadRepositoryResource(jarResource,
                     "jarrepository", "jar", "jar", options, new 
URLRepository());

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/LazyTimeoutConstraint.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/ivy/plugins/resolver/LazyTimeoutConstraint.java 
b/src/java/org/apache/ivy/plugins/resolver/LazyTimeoutConstraint.java
new file mode 100644
index 0000000..aec17e2
--- /dev/null
+++ b/src/java/org/apache/ivy/plugins/resolver/LazyTimeoutConstraint.java
@@ -0,0 +1,47 @@
+/*
+ * 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.plugins.resolver;
+
+import org.apache.ivy.core.settings.TimeoutConstraint;
+
+/**
+ * A {@link TimeoutConstraint} which determines the timeouts by invoking the 
{@link AbstractResolver underlying resolver}'s
+ * {@link AbstractResolver#getTimeoutConstraint()}, whenever the timeouts are 
requested for.
+ * This class can be used when the {@link TimeoutConstraint} is to be created 
but the underlying resolver, which decides the timeouts,
+ * hasn't yet been fully initialized
+ */
+final class LazyTimeoutConstraint implements TimeoutConstraint {
+
+    private final AbstractResolver resolver;
+
+    public LazyTimeoutConstraint(final AbstractResolver resolver) {
+        this.resolver = resolver;
+    }
+
+    @Override
+    public int getConnectionTimeout() {
+        final TimeoutConstraint resolverTimeoutConstraint = 
resolver.getTimeoutConstraint();
+        return resolverTimeoutConstraint == null ? -1 : 
resolverTimeoutConstraint.getConnectionTimeout();
+    }
+
+    @Override
+    public int getReadTimeout() {
+        final TimeoutConstraint resolverTimeoutConstraint = 
resolver.getTimeoutConstraint();
+        return resolverTimeoutConstraint == null ? -1 : 
resolverTimeoutConstraint.getReadTimeout();
+    }
+}

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
index eeb3339..947c826 100644
--- a/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/MirroredURLResolver.java
@@ -67,7 +67,7 @@ public class MirroredURLResolver extends RepositoryResolver {
                         + ", an incorrect url has been found and will then not 
be used: " + baseUrl);
             }
             if (url != null) {
-                RelativeURLRepository repo = new RelativeURLRepository(url);
+                final RelativeURLRepository repo = new 
RelativeURLRepository(url, this.getTimeoutConstraint());
                 repositories.add(repo);
             }
         }
@@ -75,11 +75,11 @@ public class MirroredURLResolver extends RepositoryResolver 
{
     }
 
     private File downloadMirrorList() {
-        URLRepository urlRepository = new URLRepository();
+        final URLRepository urlRepository = new 
URLRepository(this.getTimeoutConstraint());
         if (getEventManager() != null) {
             urlRepository.addTransferListener(getEventManager());
         }
-        URLResource mirrorResource = new URLResource(mirrorListUrl);
+        final URLResource mirrorResource = new URLResource(mirrorListUrl, 
this.getTimeoutConstraint());
         CacheResourceOptions options = new CacheResourceOptions();
         ArtifactDownloadReport report = 
getRepositoryCacheManager().downloadRepositoryResource(
             mirrorResource, "mirrorlist", "text", "txt", options, 
urlRepository);

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
index 096df8d..d06d3fc 100644
--- a/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/SFTPResolver.java
@@ -28,7 +28,7 @@ import org.apache.ivy.plugins.repository.sftp.SFTPRepository;
 public class SFTPResolver extends AbstractSshBasedResolver {
 
     public SFTPResolver() {
-        setRepository(new SFTPRepository());
+        setRepository(new SFTPRepository(new LazyTimeoutConstraint(this)));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/SshResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
index 3c68dcc..afb0dcb 100644
--- a/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/SshResolver.java
@@ -25,7 +25,7 @@ import org.apache.ivy.plugins.repository.ssh.SshRepository;
 public class SshResolver extends AbstractSshBasedResolver {
 
     public SshResolver() {
-        setRepository(new SshRepository());
+        setRepository(new SshRepository(new LazyTimeoutConstraint(this)));
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/URLResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
index d34ad33..3900845 100644
--- a/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/URLResolver.java
@@ -25,11 +25,12 @@ import org.apache.ivy.plugins.repository.url.URLRepository;
  */
 public class URLResolver extends RepositoryResolver {
     public URLResolver() {
-        setRepository(new URLRepository());
+        setRepository(new URLRepository(new LazyTimeoutConstraint(this)));
     }
 
     @Override
     public String getTypeName() {
         return "url";
     }
+
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
index 931b704..6df0753 100644
--- a/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/VfsResolver.java
@@ -31,7 +31,7 @@ public class VfsResolver extends RepositoryResolver {
     private static final int PASSWORD_GROUP = 2;
 
     public VfsResolver() {
-        setRepository(new VfsRepository());
+        setRepository(new VfsRepository(new LazyTimeoutConstraint(this)));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java 
b/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
index 0ccbacf..9bb8e7f 100644
--- a/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
+++ b/src/java/org/apache/ivy/plugins/resolver/VsftpResolver.java
@@ -25,7 +25,7 @@ import 
org.apache.ivy.plugins.repository.vsftp.VsftpRepository;
  */
 public class VsftpResolver extends RepositoryResolver {
     public VsftpResolver() {
-        setRepository(new VsftpRepository());
+        setRepository(new VsftpRepository(new LazyTimeoutConstraint(this)));
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/util/FileUtil.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/util/FileUtil.java 
b/src/java/org/apache/ivy/util/FileUtil.java
index 56ada7e..5446e9d 100644
--- a/src/java/org/apache/ivy/util/FileUtil.java
+++ b/src/java/org/apache/ivy/util/FileUtil.java
@@ -45,6 +45,7 @@ import java.util.regex.Pattern;
 import java.util.zip.GZIPInputStream;
 import java.util.zip.ZipInputStream;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
 import org.apache.ivy.util.url.URLHandlerRegistry;
 
 /**
@@ -271,12 +272,14 @@ public final class FileUtil {
         return true;
     }
 
-    public static void copy(URL src, File dest, CopyProgressListener l) throws 
IOException {
-        URLHandlerRegistry.getDefault().download(src, dest, l);
+    public static void copy(final URL src, final File dest, final 
CopyProgressListener listener,
+                            final TimeoutConstraint timeoutConstraint) throws 
IOException {
+        URLHandlerRegistry.getDefault().download(src, dest, listener, 
timeoutConstraint);
     }
 
-    public static void copy(File src, URL dest, CopyProgressListener l) throws 
IOException {
-        URLHandlerRegistry.getDefault().upload(src, dest, l);
+    public static void copy(final File src, final URL dest, final 
CopyProgressListener listener,
+                            final TimeoutConstraint timeoutConstraint) throws 
IOException {
+        URLHandlerRegistry.getDefault().upload(src, dest, listener, 
timeoutConstraint);
     }
 
     public static void copy(InputStream src, File dest, CopyProgressListener 
l) throws IOException {

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/util/StringUtils.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/util/StringUtils.java 
b/src/java/org/apache/ivy/util/StringUtils.java
index 6e4931a..5c596d4 100644
--- a/src/java/org/apache/ivy/util/StringUtils.java
+++ b/src/java/org/apache/ivy/util/StringUtils.java
@@ -189,4 +189,18 @@ public final class StringUtils {
         return sb.toString();
     }
 
+    /**
+     * Asserts that the passed <code>value</code> is not null and not an empty 
{@link String}. The implementation
+     * of this method {@link String#trim() trims} the (non-null) 
<code>value</code> to check whether the value is an
+     * empty string. If the <code>value</code> is either null or empty, then 
this method throws an {@link IllegalArgumentException}
+     * with the passed <code>errorMessage</code> as the message in the 
exception.
+     *
+     * @param value        The value to check for
+     * @param errorMessage The error message
+     */
+    public static void assertNotNullNotEmpty(final String value, final String 
errorMessage) {
+        if (value == null || value.trim().isEmpty()) {
+            throw new IllegalArgumentException(errorMessage);
+        }
+    }
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/util/url/AbstractURLHandler.java 
b/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
index 2029724..a1184d2 100644
--- a/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
+++ b/src/java/org/apache/ivy/util/url/AbstractURLHandler.java
@@ -31,6 +31,7 @@ import java.util.zip.GZIPInputStream;
 import java.util.zip.Inflater;
 import java.util.zip.InflaterInputStream;
 import org.apache.ivy.Ivy;
+import org.apache.ivy.core.settings.TimeoutConstraint;
 
 public abstract class AbstractURLHandler implements URLHandler {
 
@@ -39,28 +40,49 @@ public abstract class AbstractURLHandler implements 
URLHandler {
     // the request method to use. TODO: don't use a static here
     private static int requestMethod = REQUEST_METHOD_HEAD;
 
-    public boolean isReachable(URL url) {
-        return getURLInfo(url).isReachable();
+    @Override
+    public boolean isReachable(final URL url) {
+        return this.isReachable(url, null);
     }
 
-    public boolean isReachable(URL url, int timeout) {
-        return getURLInfo(url, timeout).isReachable();
+    @Override
+    public boolean isReachable(final URL url, final int timeout) {
+        return this.isReachable(url, createTimeoutConstraints(timeout));
     }
 
-    public long getContentLength(URL url) {
-        return getURLInfo(url).getContentLength();
+    @Override
+    public boolean isReachable(final URL url, final TimeoutConstraint 
timeoutConstraint) {
+        return this.getURLInfo(url, timeoutConstraint).isReachable();
     }
 
-    public long getContentLength(URL url, int timeout) {
-        return getURLInfo(url, timeout).getContentLength();
+    @Override
+    public long getContentLength(final URL url) {
+        return this.getContentLength(url, null);
     }
 
-    public long getLastModified(URL url) {
-        return getURLInfo(url).getLastModified();
+    @Override
+    public long getContentLength(final URL url, final int timeout) {
+        return this.getContentLength(url, createTimeoutConstraints(timeout));
     }
 
-    public long getLastModified(URL url, int timeout) {
-        return getURLInfo(url, timeout).getLastModified();
+    @Override
+    public long getContentLength(final URL url, final TimeoutConstraint 
timeoutConstraint) {
+        return this.getURLInfo(url, timeoutConstraint).getContentLength();
+    }
+
+    @Override
+    public long getLastModified(final URL url) {
+        return this.getLastModified(url, null);
+    }
+
+    @Override
+    public long getLastModified(final URL url, final int timeout) {
+        return this.getLastModified(url, createTimeoutConstraints(timeout));
+    }
+
+    @Override
+    public long getLastModified(final URL url, final TimeoutConstraint 
timeoutConstraint) {
+        return this.getURLInfo(url, timeoutConstraint).getLastModified();
     }
 
     protected String getUserAgent() {
@@ -173,4 +195,17 @@ public abstract class AbstractURLHandler implements 
URLHandler {
         return result;
     }
 
+    protected static TimeoutConstraint createTimeoutConstraints(final int 
connectionTimeout) {
+        return new TimeoutConstraint() {
+            @Override
+            public int getConnectionTimeout() {
+                return connectionTimeout;
+            }
+
+            @Override
+            public int getReadTimeout() {
+                return -1;
+            }
+        };
+    }
 }

http://git-wip-us.apache.org/repos/asf/ant-ivy/blob/6607bdcb/src/java/org/apache/ivy/util/url/BasicURLHandler.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/ivy/util/url/BasicURLHandler.java 
b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
index 97ee90f..342ad04 100644
--- a/src/java/org/apache/ivy/util/url/BasicURLHandler.java
+++ b/src/java/org/apache/ivy/util/url/BasicURLHandler.java
@@ -17,6 +17,11 @@
  */
 package org.apache.ivy.util.url;
 
+import org.apache.ivy.core.settings.TimeoutConstraint;
+import org.apache.ivy.util.CopyProgressListener;
+import org.apache.ivy.util.FileUtil;
+import org.apache.ivy.util.Message;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.File;
@@ -29,10 +34,6 @@ import java.net.URL;
 import java.net.URLConnection;
 import java.net.UnknownHostException;
 
-import org.apache.ivy.util.CopyProgressListener;
-import org.apache.ivy.util.FileUtil;
-import org.apache.ivy.util.Message;
-
 /**
  *
  */
@@ -49,28 +50,37 @@ public class BasicURLHandler extends AbstractURLHandler {
         }
     }
 
-    public URLInfo getURLInfo(URL url) {
-        return getURLInfo(url, 0);
+    @Override
+    public URLInfo getURLInfo(final URL url) {
+        return this.getURLInfo(url, null);
+    }
+
+    @Override
+    public URLInfo getURLInfo(final URL url, final int timeout) {
+        return this.getURLInfo(url, createTimeoutConstraints(timeout));
     }
 
-    public URLInfo getURLInfo(URL url, int timeout) {
+    @Override
+    public URLInfo getURLInfo(final URL url, final TimeoutConstraint 
timeoutConstraint) {
         // Install the IvyAuthenticator
         if ("http".equals(url.getProtocol()) || 
"https".equals(url.getProtocol())) {
             IvyAuthenticator.install();
         }
-
+        final int connectionTimeout = (timeoutConstraint == null || 
timeoutConstraint.getConnectionTimeout() < 0) ? 0 : 
timeoutConstraint.getConnectionTimeout();
+        final int readTimeout = (timeoutConstraint == null || 
timeoutConstraint.getReadTimeout() < 0) ? 0 : 
timeoutConstraint.getReadTimeout();
         URLConnection con = null;
         try {
-            url = normalizeToURL(url);
-            con = url.openConnection();
-            con.setConnectTimeout(timeout);
+            final URL normalizedURL = normalizeToURL(url);
+            con = normalizedURL.openConnection();
+            con.setConnectTimeout(connectionTimeout);
+            con.setReadTimeout(readTimeout);
             con.setRequestProperty("User-Agent", getUserAgent());
             if (con instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) con;
                 if (getRequestMethod() == URLHandler.REQUEST_METHOD_HEAD) {
                     httpCon.setRequestMethod("HEAD");
                 }
-                if (checkStatusCode(url, httpCon)) {
+                if (checkStatusCode(normalizedURL, httpCon)) {
                     String bodyCharset = 
getCharSetFromContentType(con.getContentType());
                     return new URLInfo(true, httpCon.getContentLength(), 
con.getLastModified(),
                             bodyCharset);
@@ -101,8 +111,7 @@ public class BasicURLHandler extends AbstractURLHandler {
      * Extract the charset from the Content-Type header string, or default to 
ISO-8859-1 as per
      * rfc2616-sec3.html#sec3.7.1 .
      *
-     * @param contentType
-     *            the Content-Type header string
+     * @param contentType the Content-Type header string
      * @return the charset as specified in the content type, or ISO-8859-1 if 
unspecified.
      */
     public static String getCharSetFromContentType(String contentType) {
@@ -148,27 +157,37 @@ public class BasicURLHandler extends AbstractURLHandler {
         return false;
     }
 
-    public InputStream openStream(URL url) throws IOException {
+    @Override
+    public InputStream openStream(final URL url) throws IOException {
+        return this.openStream(url, null);
+    }
+
+    @Override
+    public InputStream openStream(final URL url, final TimeoutConstraint 
timeoutConstraint) throws IOException {
         // Install the IvyAuthenticator
         if ("http".equals(url.getProtocol()) || 
"https".equals(url.getProtocol())) {
             IvyAuthenticator.install();
         }
+        final int connectionTimeout = (timeoutConstraint == null || 
timeoutConstraint.getConnectionTimeout() < 0) ? 0 : 
timeoutConstraint.getConnectionTimeout();
+        final int readTimeout = (timeoutConstraint == null || 
timeoutConstraint.getReadTimeout() < 0) ? 0 : 
timeoutConstraint.getReadTimeout();
 
         URLConnection conn = null;
         try {
-            url = normalizeToURL(url);
-            conn = url.openConnection();
+            final URL normalizedURL = normalizeToURL(url);
+            conn = normalizedURL.openConnection();
+            conn.setConnectTimeout(connectionTimeout);
+            conn.setReadTimeout(readTimeout);
             conn.setRequestProperty("User-Agent", getUserAgent());
             conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
             if (conn instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) conn;
-                if (!checkStatusCode(url, httpCon)) {
-                    throw new IOException("The HTTP response code for " + url
+                if (!checkStatusCode(normalizedURL, httpCon)) {
+                    throw new IOException("The HTTP response code for " + 
normalizedURL
                             + " did not indicate a success." + " See log for 
more detail.");
                 }
             }
             InputStream inStream = 
getDecodingInputStream(conn.getContentEncoding(),
-                conn.getInputStream());
+                    conn.getInputStream());
             ByteArrayOutputStream outStream = new ByteArrayOutputStream();
 
             byte[] buffer = new byte[BUFFER_SIZE];
@@ -182,30 +201,42 @@ public class BasicURLHandler extends AbstractURLHandler {
         }
     }
 
-    public void download(URL src, File dest, CopyProgressListener l) throws 
IOException {
+    @Override
+    public void download(final URL src, final File dest, final 
CopyProgressListener l) throws IOException {
+        this.download(src, dest, l, null);
+    }
+
+    @Override
+    public void download(final URL src, final File dest, final 
CopyProgressListener listener,
+                         final TimeoutConstraint timeoutConstraint) throws 
IOException {
+
         // Install the IvyAuthenticator
         if ("http".equals(src.getProtocol()) || 
"https".equals(src.getProtocol())) {
             IvyAuthenticator.install();
         }
+        final int connectionTimeout = (timeoutConstraint == null || 
timeoutConstraint.getConnectionTimeout() < 0) ? 0 : 
timeoutConstraint.getConnectionTimeout();
+        final int readTimeout = (timeoutConstraint == null || 
timeoutConstraint.getReadTimeout() < 0) ? 0 : 
timeoutConstraint.getReadTimeout();
 
         URLConnection srcConn = null;
         try {
-            src = normalizeToURL(src);
-            srcConn = src.openConnection();
+            final URL normalizedURL = normalizeToURL(src);
+            srcConn = normalizedURL.openConnection();
+            srcConn.setConnectTimeout(connectionTimeout);
+            srcConn.setReadTimeout(readTimeout);
             srcConn.setRequestProperty("User-Agent", getUserAgent());
             srcConn.setRequestProperty("Accept-Encoding", "gzip,deflate");
             if (srcConn instanceof HttpURLConnection) {
                 HttpURLConnection httpCon = (HttpURLConnection) srcConn;
-                if (!checkStatusCode(src, httpCon)) {
-                    throw new IOException("The HTTP response code for " + src
+                if (!checkStatusCode(normalizedURL, httpCon)) {
+                    throw new IOException("The HTTP response code for " + 
normalizedURL
                             + " did not indicate a success." + " See log for 
more detail.");
                 }
             }
 
             // do the download
             InputStream inStream = 
getDecodingInputStream(srcConn.getContentEncoding(),
-                srcConn.getInputStream());
-            FileUtil.copy(inStream, dest, l);
+                    srcConn.getInputStream());
+            FileUtil.copy(inStream, dest, listener);
 
             // check content length only if content was not encoded
             if (srcConn.getContentEncoding() == null) {
@@ -213,7 +244,7 @@ public class BasicURLHandler extends AbstractURLHandler {
                 if (contentLength != -1 && dest.length() != contentLength) {
                     dest.delete();
                     throw new IOException(
-                            "Downloaded file size doesn't match expected 
Content Length for " + src
+                            "Downloaded file size doesn't match expected 
Content Length for " + normalizedURL
                                     + ". Please retry.");
                 }
             }
@@ -228,7 +259,15 @@ public class BasicURLHandler extends AbstractURLHandler {
         }
     }
 
-    public void upload(File source, URL dest, CopyProgressListener l) throws 
IOException {
+    @Override
+    public void upload(final File source, final URL dest, final 
CopyProgressListener l) throws IOException {
+        this.upload(source, dest, l);
+    }
+
+    @Override
+    public void upload(final File src, final URL dest, final 
CopyProgressListener listener,
+                       final TimeoutConstraint timeoutConstraint) throws 
IOException {
+
         if (!"http".equals(dest.getProtocol()) && 
!"https".equals(dest.getProtocol())) {
             throw new UnsupportedOperationException(
                     "URL repository only support HTTP PUT at the moment");
@@ -237,32 +276,28 @@ public class BasicURLHandler extends AbstractURLHandler {
         // Install the IvyAuthenticator
         IvyAuthenticator.install();
 
+        final int connectionTimeout = (timeoutConstraint == null || 
timeoutConstraint.getConnectionTimeout() < 0) ? 0 : 
timeoutConstraint.getConnectionTimeout();
         HttpURLConnection conn = null;
         try {
-            dest = normalizeToURL(dest);
-            conn = (HttpURLConnection) dest.openConnection();
+            final URL normalizedDestURL = normalizeToURL(dest);
+            conn = (HttpURLConnection) normalizedDestURL.openConnection();
             conn.setDoOutput(true);
+            conn.setConnectTimeout(connectionTimeout);
             conn.setRequestMethod("PUT");
             conn.setRequestProperty("User-Agent", getUserAgent());
             conn.setRequestProperty("Content-type", 
"application/octet-stream");
-            conn.setRequestProperty("Content-length", 
Long.toString(source.length()));
+            conn.setRequestProperty("Content-length", 
Long.toString(src.length()));
             conn.setInstanceFollowRedirects(true);
 
-            InputStream in = new FileInputStream(source);
-            try {
-                OutputStream os = conn.getOutputStream();
-                FileUtil.copy(in, os, l);
-            } finally {
-                try {
-                    in.close();
-                } catch (IOException e) {
-                    /* ignored */
-                }
+            try (final InputStream in = new FileInputStream(src);) {
+                final OutputStream os = conn.getOutputStream();
+                FileUtil.copy(in, os, listener);
             }
-            validatePutStatusCode(dest, conn.getResponseCode(), 
conn.getResponseMessage());
+            validatePutStatusCode(normalizedDestURL, conn.getResponseCode(), 
conn.getResponseMessage());
         } finally {
             disconnect(conn);
         }
+
     }
 
     private void disconnect(URLConnection con) {

Reply via email to