Index: httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java (revision 1405730) +++ httpclient/src/main/java/org/apache/http/impl/conn/HttpClientConnectionManagerBase.java (revision ) @@ -74,6 +74,7 @@ } this.pool = pool; this.connectionOperator = new HttpClientConnectionOperator(schemeRegistry, dnsResolver); + ClientConnectionManagerTracker.getTracker().add(this); } @Override Index: httpclient/src/main/java/org/apache/http/conn/proxy/ConfigurableProxySelector.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/conn/proxy/ConfigurableProxySelector.java (revision ) +++ httpclient/src/main/java/org/apache/http/conn/proxy/ConfigurableProxySelector.java (revision ) @@ -0,0 +1,136 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.conn.proxy; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * The ProxyConfigurationManager holds all known {@link ProxyConfiguration}s. + */ +public class ConfigurableProxySelector extends ProxySelector { + + private static final int MAX_FAILURES = 3; + + public static ConfigurableProxySelector instance; + + private final Log log = LogFactory.getLog(getClass()); + private final Set configs = new HashSet(); + + /** + * Returns a singleton ProxyConfigurationManager instance. The first call will construct the + * singleton. + * + * @return The ProxyConfigurationManager + */ + public static ConfigurableProxySelector getSelector() { + if (null == instance) { + instance = new ConfigurableProxySelector(); + } + return instance; + } + + /** + * Adds the given {@link ProxyConfiguration} to the configs held by this manager, replacing the config if already + * present. + * + * @param configuration The proxy configuration. + */ + public void addProxyConfiguration(final ProxyConfiguration configuration) { + synchronized (configs) { + removeProxyConfiguration(configuration); + log.info("added proxy configuration: " + configuration + "(now " + configs.size() + " configs)"); + configs.add(configuration); + } + } + + /** + * Removes the given {@link ProxyConfiguration} from this manager. + * + * @param configuration The configuration to remove. + */ + public void removeProxyConfiguration(final ProxyConfiguration configuration) { + synchronized (configs) { + log.info("removed proxy configuration: " + configuration + "(now " + configs.size() + " configs)"); + configs.remove(configuration); + } + } + + @Override + public List select(final URI uri) { + if (null == uri) { + throw new IllegalArgumentException("URI must not be null"); + } + + final List proxies = new ArrayList(); + + for (final ProxyConfiguration config : configs) { + if (config.accepts(uri)) { + proxies.add(config.getProxy()); + } + } + + if (proxies.size() == 0) { + proxies.add(Proxy.NO_PROXY); + } + + return proxies; + } + + @Override + public void connectFailed(final URI uri, final SocketAddress address, final IOException ioe) { + + if (null == uri || null == address || null == ioe) { + throw new IllegalArgumentException("arguments may not be null."); + } + + final Iterator iterator = configs.iterator(); + while (iterator.hasNext()) { + final ProxyConfiguration config = iterator.next(); + if (address.equals(config.getAddress())) { + final int failures = config.failed(); + log.error("Connection to proxy [" + config + "] failed (" + failures + " time(s))."); + if (failures >= MAX_FAILURES) { + iterator.remove(); + log.error("Proxy [" + config + "] failed more than " + MAX_FAILURES + " times, removed."); + } + break; + } + } + } +} Index: httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java (revision 1405730) +++ httpclient/src/main/java/org/apache/http/conn/routing/HttpRoute.java (revision ) @@ -27,9 +27,17 @@ package org.apache.http.conn.routing; +import java.lang.reflect.UndeclaredThrowableException; import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.List; import org.apache.http.annotation.Immutable; +import org.apache.http.conn.proxy.ConfigurableProxySelector; import org.apache.http.util.LangUtils; import org.apache.http.HttpHost; @@ -103,6 +111,32 @@ if ((tunnelled == TunnelType.TUNNELLED) && (proxies.length == 0)) { throw new IllegalArgumentException ("Proxy required if tunnelled."); + } + + // select a proxy from the configurable proxy selector. e.g. in an OSGi-environment, + // the proxy selector is set fed with OSGi-originating configurations, + // and thus provides centrally configured proxies without the knowledge of the API user + if (proxies.length == 0) { + try { + // get list of proxies from the default proxy selector + final List defaultProxyList = + ConfigurableProxySelector.getSelector().select(new URI(target.toURI())); + final ArrayList defaultProxyHosts = new ArrayList(); + for (final Proxy proxy : defaultProxyList) { + // downcast to InetSocketAddress for host and port accessors + final InetSocketAddress address = (InetSocketAddress) proxy.address(); + + // only HTTP proxies are supported, the address of which is not null + if (Proxy.Type.HTTP == proxy.type() && null != address) { + defaultProxyHosts.add(new HttpHost(address.getHostName(), address.getPort())); + } + } + + proxies = defaultProxyHosts.toArray(new HttpHost[defaultProxyHosts.size()]); + + } catch (URISyntaxException e) { + throw new UndeclaredThrowableException(e); + } } // tunnelled is already checked above, that is in line with the default Index: httpclient-osgi/pom.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient-osgi/pom.xml (revision 1405730) +++ httpclient-osgi/pom.xml (revision ) @@ -72,6 +72,18 @@ ${project.version} compile + + org.osgi + org.osgi.core + 4.2.0 + provided + + + org.osgi + org.osgi.compendium + 4.2.0 + provided + @@ -88,6 +100,7 @@ true **/*.properties + **/metatype.* @@ -98,6 +111,7 @@ true + org.apache.http.osgi.Activator Apache ${project.name} ${project.groupId}.httpclient <_exportcontents> @@ -117,6 +131,8 @@ javax.crypto.spec, javax.net.ssl,javax.security.auth.x500, org.ietf.jgss, + org.osgi.framework, + org.osgi.service.cm, org.apache.commons.logging;version=${commons-logging.version}, org.apache.http;version=${httpcore.version}, org.apache.http.concurrent;version=${httpcore.version}, Index: httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java (revision 1405730) +++ httpclient/src/main/java/org/apache/http/impl/conn/BasicHttpClientConnectionManager.java (revision ) @@ -103,6 +103,7 @@ this.connectionOperator = new HttpClientConnectionOperator(schemeRegistry, dnsResolver); this.connFactory = connFactory != null ? connFactory : DefaultClientConnectionFactory.INSTANCE; this.expiry = Long.MAX_VALUE; + ClientConnectionManagerTracker.getTracker().add(this); } public BasicHttpClientConnectionManager(final SchemeRegistry schemeRegistry) { Index: httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.xml IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.xml (revision ) +++ httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.xml (revision ) @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Index: httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.properties IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>MacRoman =================================================================== --- httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.properties (revision ) +++ httpclient-osgi/src/main/resources/OSGI-INF/metatype/metatype.properties (revision ) @@ -0,0 +1,58 @@ +proxyconfigurator.name = Apache HTTP Components Proxy Configurator +proxyconfigurator.description = Factory configuration for transparent proxies used by every HTTP Client. + +proxyconfig.name = Apache HTTP Components Proxy Configuration +proxyconfig.description = Proxy configuration for central and transparent proxying of http client connections. + +proxy.enabled.name = Enable HTTP Proxy +proxy.enabled.description = Whether to enable or disable this particular proxy configuration. \ +The default value is false. + +proxy.host.name = HTTP Proxy Host +proxy.host.description = Host name (or IP Address) of the HTTP Proxy. This property is ignored if \ + this proxy configuration is disabled. This property does not have a default value. Enabling \ + this proxy but not setting the HTTP Proxy Host effectively disables this configuration. + +proxy.port.name = HTTP Proxy Port +proxy.port.description = TCP port of the HTTP Proxy. This property is ignored if \ + this proxy configuration is disabled. This property does not have a default value. Enabling \ + this proxy but not setting the HTTP Proxy Port effectively disables this configuration. + +proxy.user.name = HTTP Proxy User +proxy.user.description = The name of the user to authenticate as with the HTTP \ + Proxy Host. If this field is empty, the proxy is considered to not be \ + authenticated. The default is empty. This property is ignored if proxying is \ + disabled or the proxy host is not properly configured. + +proxy.password.name = HTTP Proxy Password +proxy.password.description = The password of the HTTP Proxy user to authenticate \ + with. The default is empty. This property is ignored if proxying is \ + disabled or the proxy host is not properly configured. + +proxy.ntlm.host.name = HTTP Proxy NTLM Host +proxy.ntlm.host.description = The host the authentication request is \ + originating from. Essentially, the computer name for this machine. By default \ + the credentials assume simple username password authentication. If the proxy \ + happens to be a Microsoft IIS Server using NTLM authentication this property \ + must be set to the NT Domain name of the user to authenticate as. This is \ + not set by default. + +proxy.ntlm.domain.name = HTTP Proxy NTLM Domain +proxy.ntlm.domain.description = The NTLM domain to authenticate within. By \ + default the credentials assume simple username password authentication. If \ + the proxy happens to be a Microsoft IIS Server using NTLM authentication this \ + property must be set to the NT Domain name of the user to authenticate as. \ + This is not set by default. + +proxy.exceptions.name = No Proxy For +proxy.exceptions.description = Lists domain names, host names, IP Addresses or \ + or network addresses for which this proxy configuration should not be used. A domain \ + name indicating all hosts of a domain is indicated by a leading dot, e.g. \ + ".day.com". A network address is indicated with subnet mask notation indicating \ + the number of bits make up the network address, e.g 192.168.1.0/24 means the \ + class C network "192.168.1". Note that for proxy selection, the host name of \ + URL is not resolved but directly compared to the list of exceptions. For this \ + reason you might want to indicate both the network address and the domain for \ + targets which should not be passed through the proxy. This property has no \ + effect if this proxy configuration is disabled. The default value is [ localhost, \ + 127.0.0.1 ]. Index: httpclient/src/main/java/org/apache/http/impl/conn/ClientConnectionManagerTracker.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/impl/conn/ClientConnectionManagerTracker.java (revision ) +++ httpclient/src/main/java/org/apache/http/impl/conn/ClientConnectionManagerTracker.java (revision ) @@ -0,0 +1,91 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.conn; + +import org.apache.http.conn.ClientConnectionManager; +import org.apache.http.conn.HttpClientConnectionManager; + +import java.util.Set; +import java.util.WeakHashMap; + +/** + * The ClientConnectionManagerTracker should be used by any implementations of {@link + * HttpClientConnectionManager}, which should in the constructor add its instance via {@link + * #add(org.apache.http.conn.HttpClientConnectionManager)}: + *

+ * ClientConnectionManagerTracker.getInstance().add(this); + *

+ * ClientConnectionManagers added thus are tracked using {@link java.lang.ref.WeakReference}s. The purpose of the + * tracking is such, that all active ClientConnectionManagers can be retrieved via {@link #getAll()} and tasks, such as + * shutting down all clients in order to ensure proper stopping of an application using this library. + */ +public final class ClientConnectionManagerTracker { + + public static ClientConnectionManagerTracker instance; + + private final WeakHashMap managers + = new WeakHashMap(); + + // do not allow public construction + private ClientConnectionManagerTracker() { + } + + /** + * Returns a singleton ClientConnectionManagerTracker instance. The first call will construct the + * singleton. + * + * @return The ClientConnectionManagerTracker + */ + public static ClientConnectionManagerTracker getTracker() { + if (null == instance) { + instance = new ClientConnectionManagerTracker(); + } + return instance; + } + + /** + * Adds a {@link HttpClientConnectionManager} as a {@link java.lang.ref.WeakReference} to this tracker. + * + * @param manager The {@link HttpClientConnectionManager} to add. + */ + public void add(final HttpClientConnectionManager manager) { + synchronized (managers) { + // only the key is weakly referenced, the map value can be ignored + managers.put(manager, null); + } + } + + /** + * Returnes a {@link Set} of {@link java.lang.ref.WeakReference}s to {@link ClientConnectionManager}s. An element of + * the set may be null. + * + * @return A Set representing weakly referenced ClientConnectionManagers. + */ + public Set getAll() { + return managers.keySet(); + } +} Index: httpclient/src/main/java/org/apache/http/impl/conn/proxy/ProxyConfigurationImpl.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/impl/conn/proxy/ProxyConfigurationImpl.java (revision ) +++ httpclient/src/main/java/org/apache/http/impl/conn/proxy/ProxyConfigurationImpl.java (revision ) @@ -0,0 +1,287 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.conn.proxy; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.client.utils.PropertiesUtil; +import org.apache.http.conn.proxy.ProxyConfiguration; + +import java.net.InetSocketAddress; +import java.net.Proxy; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.Dictionary; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A ProxyConfiguration no proxy (not this one) + for (final HostMatcher matcher : exceptions) { + if (matcher.matches(uri.getHost())) { + return false; + } + } + + } else { + return false; + } + + return true; + } + + private void setProxyExceptions(String[] exceptions) { + ArrayList exceptionList = new ArrayList(); + if (exceptions != null) { + for (final String exception : exceptions) { + exceptionList.add(createMatcher(exception.trim())); + } + } + this.exceptions = exceptionList.toArray(new HostMatcher[exceptionList.size()]); + } + + private static HostMatcher createMatcher(final String name) { + final NetworkAddress na = NetworkAddress.parse(name); + if (na != null) { + return new IPAddressMatcher(na); + } + + if (name.startsWith(".")) { + return new DomainNameMatcher(name); + } + + return new HostNameMatcher(name); + } + + private static interface HostMatcher { + + boolean matches(String host); + } + + private static class HostNameMatcher implements HostMatcher { + + private final String hostName; + + HostNameMatcher(String hostName) { + this.hostName = hostName; + } + + public boolean matches(String host) { + return hostName.equalsIgnoreCase(host); + } + } + + private static class DomainNameMatcher implements HostMatcher { + + private final String domainName; + + DomainNameMatcher(String domainName) { + this.domainName = domainName.toLowerCase(); + } + + public boolean matches(String host) { + return host.toLowerCase().endsWith(domainName); + } + } + + private static class IPAddressMatcher implements HostMatcher { + + private final NetworkAddress address; + + IPAddressMatcher(NetworkAddress address) { + this.address = address; + } + + public boolean matches(String host) { + NetworkAddress hostAddress = NetworkAddress.parse(host); + return hostAddress != null && address.address == (hostAddress.address & address.mask); + } + } + + private static class NetworkAddress { + + final int address; + + final int mask; + + static NetworkAddress parse(String adrSpec) { + + if (null != adrSpec) { + final Matcher nameMatcher = IP_MASK_PATTERN.matcher(adrSpec); + if (nameMatcher.matches()) { + try { + int i1 = toInt(nameMatcher.group(1), 255); + int i2 = toInt(nameMatcher.group(2), 255); + int i3 = toInt(nameMatcher.group(3), 255); + int i4 = toInt(nameMatcher.group(4), 255); + int ip = i1 << 24 | i2 << 16 | i3 << 8 | i4; + + int mask = toInt(nameMatcher.group(6), 32); + mask = (mask == 32) ? -1 : -1 - (-1 >>> mask); + + return new NetworkAddress(ip, mask); + } catch (NumberFormatException nfe) { + // not expected after the pattern match ! + } + } + } + + return null; + } + + private static int toInt(String value, int max) { + if (value == null || value.length() == 0) { + return max; + } + + int number = Integer.parseInt(value); + if (number > max) { + number = max; + } + return number; + } + + NetworkAddress(int address, int mask) { + this.address = address; + this.mask = mask; + } + } +} Index: httpclient/src/main/java/org/apache/http/conn/proxy/ProxyConfiguration.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/conn/proxy/ProxyConfiguration.java (revision ) +++ httpclient/src/main/java/org/apache/http/conn/proxy/ProxyConfiguration.java (revision ) @@ -0,0 +1,111 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.conn.proxy; + +import java.net.Proxy; +import java.net.SocketAddress; +import java.net.URI; +import java.util.Dictionary; + +/** + * ProxyConfiguration... + */ +public interface ProxyConfiguration { + + /** + * Property indicating whether this particular proxy is enabled (shall be used or not). Defaults to true. + */ + String PROPERTYNAME_PROXY_ENABLED = "proxy.enabled"; + /** + * Property representing the hostname of the proxy. Defaults to empty. + */ + String PROPERTYNAME_PROXY_HOSTNAME = "proxy.host"; + /** + * Property representing the port of the proxy. Defaults to 0. + */ + String PROPERTYNAME_PROXY_PORT = "proxy.port"; + /** + * Property representing the username to authenticate with towards the proxy. Defaults to empty. + */ + String PROPERTYNAME_PROXY_USERNAME = "proxy.user"; + /** + * Property representing the password to authenticate with towards the proxy. Defaults to empty. + */ + String PROPERTYNAME_PROXY_PASSWORD = "proxy.password"; + /** + * Property representing the NTLM domain to use for authentication towards the proxy. If this is set, authentication + * is switched from basic to NTLM. + */ + String PROPERTYNAME_PROXY_NTLMHOST = "proxy.ntlm.host"; + /** + * A multivalue property representing host patterns for which no proxy shall be used. By default localhost is + * excluded. + */ + String PROPERTYNAME_PROXY_EXCEPTIONS = "proxy.exceptions"; + + void update(Dictionary config); + + SocketAddress getAddress(); + + String getHostname(); + + int getPort(); + + String getUsername(); + + String getPassword(); + + String getNtlmhost(); + + Proxy getProxy(); + + /** + * Indicates whether this config is valid and can be used. A valid configuration is enabled by configuration and has + * a valid proxy address configured. + * + * @return true if the proxy configuration is valid. + */ + boolean isValid(); + + /** + * Indicates the number of times connecting to the proxy based on this configuration has failed. + * + * @return An int representing the number of connection failures. + */ + int failed(); + + /** + * Indicates whether the given uri is accepted by this proxy configuration, i.e. that the proxy is + * suitable for usage with that URI. All URIs of scheme HTTP and HTTPS are accepted. Additionally the hostname of + * the URI must not be on the configurable exclusion list. By default localhost is exluced. + * + * @param uri The {@link java.net.URI} to check. + * + * @return true if the URI is accepted. + */ + boolean accepts(URI uri); +} Index: httpclient/src/main/java/org/apache/http/impl/conn/proxy/TransparentProxySelector.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/impl/conn/proxy/TransparentProxySelector.java (revision ) +++ httpclient/src/main/java/org/apache/http/impl/conn/proxy/TransparentProxySelector.java (revision ) @@ -0,0 +1,113 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.impl.conn.proxy; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.http.conn.proxy.ConfigurableProxySelector; + +import java.io.IOException; +import java.net.Proxy; +import java.net.ProxySelector; +import java.net.SocketAddress; +import java.net.URI; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TransparentProxySelector extends ProxySelector { + + private final Log log = LogFactory.getLog(getClass()); + + private static int MAX_FAILURES = 3; + + /** + * The map holding valid and enabled proxy configurations + */ + private final Map proxyConfigurations = new HashMap(); + private final ConfigurableProxySelector manager; + + // ------- Constructor + + public TransparentProxySelector(final ConfigurableProxySelector manager) { + this.manager = manager; + } + + // ------- API + + @Override + public List select(final URI uri) { + + if (null == uri) { + throw new IllegalArgumentException("URI must not be null"); + } + + final List proxies = new ArrayList(); + + for (final ProxyConfigurationImpl config : proxyConfigurations.values()) { + if (config.accepts(uri)) { + proxies.add(config.getProxy()); + } + } + + if (proxies.size() == 0) { + return ProxySelector.getDefault().select(uri); + } + + return proxies; + } + + @Override + public void connectFailed(final URI uri, final SocketAddress address, final IOException ioe) { + + if (null == uri || null == address || null == ioe) { + throw new IllegalArgumentException("arguments may not be null."); + } + + String toBeRemoved = null; + + for (final Map.Entry entry : proxyConfigurations.entrySet()) { + final ProxyConfigurationImpl config = entry.getValue(); + if (address.equals(config.getAddress())) { + final int failures = config.failed(); + log.error("Connection to proxy [" + config + "] failed (" + failures + " time(s))."); + if (failures >= MAX_FAILURES) { + toBeRemoved = entry.getKey(); + log.error("Proxy [" + config + "] failed more than " + MAX_FAILURES + " times, removed."); + } + break; + } + } + + if (null != toBeRemoved) { + synchronized (proxyConfigurations) { + proxyConfigurations.remove(toBeRemoved); + } + } + } +} Index: httpclient-osgi/src/main/java/org/apache/http/osgi/Activator.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient-osgi/src/main/java/org/apache/http/osgi/Activator.java (revision ) +++ httpclient-osgi/src/main/java/org/apache/http/osgi/Activator.java (revision ) @@ -0,0 +1,129 @@ +/* + * ==================================================================== + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + * + */ +package org.apache.http.osgi; + +import org.apache.http.conn.HttpClientConnectionManager; +import org.apache.http.conn.proxy.ConfigurableProxySelector; +import org.apache.http.conn.proxy.ProxyConfiguration; +import org.apache.http.impl.conn.ClientConnectionManagerTracker; +import org.apache.http.impl.conn.proxy.ProxyConfigurationImpl; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.cm.ConfigurationException; +import org.osgi.service.cm.ManagedServiceFactory; + +import java.util.Dictionary; +import java.util.HashMap; +import java.util.Hashtable; + +/** + * This is the bundle activator. It registers the activator as a service factory, so it can receive proxy + * configurations. Once a configuration is updated, it registers a proxy configuration with the {@link + * org.apache.http.conn.proxy.ConfigurableProxySelector} based on the configuration properties. + *

+ * The activator also is responsible for unregistering the service factory and proxy configurations upon bundle + * shutdown. Additionally the shutdown ensures that all open connections of all http-clients are terminated, so the OSGi + * container may shutdown irrespective of any open HTTP client connections. + */ +public class Activator implements BundleActivator, ManagedServiceFactory { + + private static final String SERVICE_FACTORY_NAME = "Apache HTTP Client Proxy Configuration Factory"; + private static final String SERVICE_PID = "org.apache.http.proxyconfigurator"; + + private ServiceRegistration configurator; + private ConfigurableProxySelector manager; + private HashMap serviceRegistrations; + private BundleContext context; + + public void start(final BundleContext context) throws Exception { + + this.context = context; + this.serviceRegistrations = new HashMap(); + this.manager = ConfigurableProxySelector.getSelector(); + + // ensure we receive configurations for the proxy selector + final Hashtable props = new Hashtable(); + props.put(Constants.SERVICE_PID, getName()); + props.put(Constants.SERVICE_VENDOR, context.getBundle().getHeaders(Constants.BUNDLE_VENDOR)); + props.put(Constants.SERVICE_DESCRIPTION, SERVICE_FACTORY_NAME); + + configurator = context.registerService(ManagedServiceFactory.class.getName(), this, props); + } + + public void stop(final BundleContext context) throws Exception { + + // unregister services + for (final ServiceRegistration service : serviceRegistrations.values()) { + service.unregister(); + } + + // unregister service factory + if (configurator != null) { + configurator.unregister(); + } + + // ensure all http clients are terminated + for (final HttpClientConnectionManager manager : ClientConnectionManagerTracker.getTracker().getAll()) { + if (null != manager) { + manager.shutdown(); + } + } + } + + public String getName() { + return SERVICE_PID; + } + + public void updated(final String serviceId, final Dictionary config) throws ConfigurationException { + + final ProxyConfiguration proxyConfiguration; + final ServiceRegistration registration = serviceRegistrations.get(serviceId); + + if (null == registration) { + proxyConfiguration = new ProxyConfigurationImpl(); + serviceRegistrations.put(serviceId, context.registerService(ProxyConfiguration.class.getName(), + proxyConfiguration, + config)); + } else { + proxyConfiguration = (ProxyConfiguration) context.getService(registration.getReference()); + } + + proxyConfiguration.update(config); + manager.addProxyConfiguration(proxyConfiguration); + } + + public void deleted(final String serviceId) { + final ServiceRegistration registration = serviceRegistrations.get(serviceId); + if (null != registration) { + manager.removeProxyConfiguration((ProxyConfiguration) context.getService(registration.getReference())); + registration.unregister(); + serviceRegistrations.remove(serviceId); + } + } +} Index: httpclient/src/main/java/org/apache/http/client/utils/PropertiesUtil.java IDEA additional info: Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP <+>UTF-8 =================================================================== --- httpclient/src/main/java/org/apache/http/client/utils/PropertiesUtil.java (revision ) +++ httpclient/src/main/java/org/apache/http/client/utils/PropertiesUtil.java (revision ) @@ -0,0 +1,219 @@ +/* + * 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.http.client.utils; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * The PropertiesUtil is a utility class providing some + * usefull utility methods for converting property types. + * + * @since 2.1 + */ +public class PropertiesUtil { + + /** + * Returns the boolean value of the parameter or the + * defaultValue if the parameter is null. + * If the parameter is not a Boolean it is converted + * by calling Boolean.valueOf on the string value of the + * object. + * @param propValue the property value or null + * @param defaultValue the default boolean value + */ + public static boolean toBoolean(Object propValue, boolean defaultValue) { + propValue = toObject(propValue); + if (propValue instanceof Boolean) { + return (Boolean) propValue; + } else if (propValue != null) { + return Boolean.valueOf(String.valueOf(propValue)); + } + + return defaultValue; + } + + /** + * Returns the parameter as a string or the + * defaultValue if the parameter is null. + * @param propValue the property value or null + * @param defaultValue the default string value + */ + public static String toString(Object propValue, String defaultValue) { + propValue = toObject(propValue); + return (propValue != null) ? propValue.toString() : defaultValue; + } + + /** + * Returns the parameter as a long or the + * defaultValue if the parameter is null or if + * the parameter is not a Long and cannot be converted to + * a Long from the parameter's string value. + * @param propValue the property value or null + * @param defaultValue the default long value + */ + public static long toLong(Object propValue, long defaultValue) { + propValue = toObject(propValue); + if (propValue instanceof Long) { + return (Long) propValue; + } else if (propValue != null) { + try { + return Long.valueOf(String.valueOf(propValue)); + } catch (NumberFormatException nfe) { + // don't care, fall through to default value + } + } + + return defaultValue; + } + + /** + * Returns the parameter as an integer or the + * defaultValue if the parameter is null or if + * the parameter is not an Integer and cannot be converted to + * an Integer from the parameter's string value. + * @param propValue the property value or null + * @param defaultValue the default integer value + */ + public static int toInteger(Object propValue, int defaultValue) { + propValue = toObject(propValue); + if (propValue instanceof Integer) { + return (Integer) propValue; + } else if (propValue != null) { + try { + return Integer.valueOf(String.valueOf(propValue)); + } catch (NumberFormatException nfe) { + // don't care, fall through to default value + } + } + + return defaultValue; + } + + /** + * Returns the parameter as a double or the + * defaultValue if the parameter is null or if + * the parameter is not a Double and cannot be converted to + * a Double from the parameter's string value. + * @param propValue the property value or null + * @param defaultValue the default double value + */ + public static double toDouble(Object propValue, double defaultValue) { + propValue = toObject(propValue); + if (propValue instanceof Double) { + return (Double) propValue; + } else if (propValue != null) { + try { + return Double.valueOf(String.valueOf(propValue)); + } catch (NumberFormatException nfe) { + // don't care, fall through to default value + } + } + + return defaultValue; + } + + /** + * Returns the parameter as a single value. If the + * parameter is neither an array nor a java.util.Collection the + * parameter is returned unmodified. If the parameter is a non-empty array, + * the first array element is returned. If the property is a non-empty + * java.util.Collection, the first collection element is returned. + * Otherwise null is returned. + * @param propValue the parameter to convert. + */ + public static Object toObject(Object propValue) { + if (propValue == null) { + return null; + } else if (propValue.getClass().isArray()) { + Object[] prop = (Object[]) propValue; + return prop.length > 0 ? prop[0] : null; + } else if (propValue instanceof Collection) { + Collection prop = (Collection) propValue; + return prop.isEmpty() ? null : prop.iterator().next(); + } else { + return propValue; + } + } + + /** + * Returns the parameter as an array of Strings. If + * the parameter is a scalar value its string value is returned as a single + * element array. If the parameter is an array, the elements are converted to + * String objects and returned as an array. If the parameter is a collection, the + * collection elements are converted to String objects and returned as an array. + * Otherwise (if the parameter is null) null is + * returned. + * @param propValue The object to convert. + */ + public static String[] toStringArray(Object propValue) { + return toStringArray(propValue, null); + } + + /** + * Returns the parameter as an array of Strings. If + * the parameter is a scalar value its string value is returned as a single + * element array. If the parameter is an array, the elements are converted to + * String objects and returned as an array. If the parameter is a collection, the + * collection elements are converted to String objects and returned as an array. + * Otherwise (if the property is null) a provided default value is + * returned. + * @param propValue The object to convert. + * @param defaultArray The default array to return. + */ + public static String[] toStringArray(Object propValue, String[] defaultArray) { + if (propValue == null) { + // no value at all + return defaultArray; + + } else if (propValue instanceof String) { + // single string + return new String[] { (String) propValue }; + + } else if (propValue instanceof String[]) { + // String[] + return (String[]) propValue; + + } else if (propValue.getClass().isArray()) { + // other array + Object[] valueArray = (Object[]) propValue; + List values = new ArrayList(valueArray.length); + for (Object value : valueArray) { + if (value != null) { + values.add(value.toString()); + } + } + return values.toArray(new String[values.size()]); + + } else if (propValue instanceof Collection) { + // collection + Collection valueCollection = (Collection) propValue; + List valueList = new ArrayList(valueCollection.size()); + for (Object value : valueCollection) { + if (value != null) { + valueList.add(value.toString()); + } + } + return valueList.toArray(new String[valueList.size()]); + } + + return defaultArray; + } +} \ No newline at end of file