| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. 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. For additional information regarding |
| * copyright in this work, please see the NOTICE file in the top level |
| * directory of this distribution. |
| */ |
| package org.apache.abdera2.protocol.client; |
| |
| import java.net.ProxySelector; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.List; |
| |
| import org.apache.abdera2.common.anno.AnnoUtil; |
| import org.apache.abdera2.common.anno.Version; |
| import org.apache.http.HttpHost; |
| import org.apache.http.HttpRequestInterceptor; |
| import org.apache.http.HttpResponseInterceptor; |
| import org.apache.http.auth.AuthScope; |
| import org.apache.http.auth.Credentials; |
| import org.apache.http.auth.params.AuthPNames; |
| import org.apache.http.client.CookieStore; |
| import org.apache.http.client.HttpClient; |
| import org.apache.http.client.params.ClientPNames; |
| import org.apache.http.client.params.CookiePolicy; |
| import org.apache.http.conn.ClientConnectionManager; |
| import org.apache.http.conn.params.ConnRoutePNames; |
| import org.apache.http.conn.scheme.PlainSocketFactory; |
| import org.apache.http.conn.scheme.Scheme; |
| import org.apache.http.conn.scheme.SchemeRegistry; |
| import org.apache.http.conn.scheme.SchemeSocketFactory; |
| import org.apache.http.conn.ssl.SSLSocketFactory; |
| import org.apache.http.cookie.Cookie; |
| import org.apache.http.impl.client.BasicCookieStore; |
| import org.apache.http.impl.client.DefaultHttpClient; |
| import org.apache.http.impl.conn.ProxySelectorRoutePlanner; |
| import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager; |
| import org.apache.http.impl.cookie.BasicClientCookie; |
| import org.apache.http.params.CoreConnectionPNames; |
| import org.apache.http.params.CoreProtocolPNames; |
| import org.apache.http.params.HttpParams; |
| |
| @Version(value="v2.0-SNAPSHOT", |
| name="Abdera", |
| uri="http://abdera.apache.org") |
| @SuppressWarnings("unchecked") |
| public class BasicClient implements Client { |
| |
| protected HttpClient client; |
| |
| public BasicClient() { |
| this(DEFAULT_USER_AGENT); |
| } |
| |
| public BasicClient(String useragent) { |
| this.client = initClient(useragent); |
| } |
| |
| public BasicClient(DefaultHttpClient client) { |
| this.client = client; |
| } |
| |
| /** |
| * Default initialization of the Scheme Registry, subclasses |
| * may overload this to customize the scheme registry |
| * configuration |
| */ |
| protected SchemeRegistry initSchemeRegistry() { |
| SchemeRegistry schemeRegistry = new SchemeRegistry(); |
| schemeRegistry.register( |
| new Scheme( |
| "http", |
| 80, |
| PlainSocketFactory.getSocketFactory())); |
| schemeRegistry.register( |
| new Scheme( |
| "https", |
| 443, |
| SSLSocketFactory.getSocketFactory())); |
| return schemeRegistry; |
| } |
| |
| /** |
| * Default initialization of the Client Connection Manager, |
| * subclasses may overload this to customize the connection |
| * manager configuration |
| */ |
| protected ClientConnectionManager initConnectionManager(SchemeRegistry sr) { |
| ThreadSafeClientConnManager cm = |
| new ThreadSafeClientConnManager(sr); |
| cm.setDefaultMaxPerRoute(initDefaultMaxConnectionsPerRoute()); |
| cm.setMaxTotal(initDefaultMaxTotalConnections()); |
| return cm; |
| } |
| |
| protected int initDefaultMaxConnectionsPerRoute() { |
| return DEFAULT_MAX_CONNECTIONS_PER_ROUTE; |
| } |
| |
| protected int initDefaultMaxTotalConnections() { |
| return DEFAULT_MAX_TOTAL_CONNECTIONS; |
| } |
| |
| protected void initDefaultParameters(HttpParams params) { |
| params.setParameter( |
| CoreProtocolPNames.USE_EXPECT_CONTINUE, |
| Boolean.TRUE); |
| params.setParameter( |
| ClientPNames.MAX_REDIRECTS, |
| DEFAULT_MAX_REDIRECTS); |
| params.setParameter( |
| ClientPNames.COOKIE_POLICY, |
| CookiePolicy.BROWSER_COMPATIBILITY); |
| } |
| |
| protected HttpClient initClient(String useragent) { |
| SchemeRegistry schemeRegistry = |
| initSchemeRegistry(); |
| ClientConnectionManager cm = |
| initConnectionManager(schemeRegistry); |
| DefaultHttpClient client = new DefaultHttpClient(cm); |
| HttpParams params = client.getParams(); |
| params.setParameter(CoreProtocolPNames.USER_AGENT, useragent); |
| initDefaultParameters(params); |
| return client; |
| } |
| |
| public <T extends Client>T addRequestInterceptor( |
| HttpRequestInterceptor i, |
| int index) { |
| if (index == -1) |
| getDefaultHttpClient().addRequestInterceptor(i); |
| else |
| getDefaultHttpClient().addRequestInterceptor(i, index); |
| return (T)this; |
| } |
| |
| public <T extends Client>T addResponseInterceptor( |
| HttpResponseInterceptor i, |
| int index) { |
| if (index == -1) |
| getDefaultHttpClient().addResponseInterceptor(i); |
| else |
| getDefaultHttpClient().addResponseInterceptor(i, index); |
| return (T)this; |
| } |
| |
| public <T extends Client>T clearRequestInterceptors() { |
| getDefaultHttpClient().clearRequestInterceptors(); |
| return (T)this; |
| } |
| |
| public <T extends Client>T clearResponseInterceptors() { |
| getDefaultHttpClient().clearResponseInterceptors(); |
| return (T)this; |
| } |
| |
| public <T extends Client>T registerScheme( |
| String scheme, |
| int port, |
| SchemeSocketFactory factory) { |
| SchemeRegistry sr = |
| getClient().getConnectionManager().getSchemeRegistry(); |
| sr.register(new Scheme(scheme,port,factory)); |
| return (T)this; |
| } |
| |
| public HttpClient getClient() { |
| return client; |
| } |
| |
| public DefaultHttpClient getDefaultHttpClient() { |
| return (DefaultHttpClient)client; |
| } |
| |
| /** |
| * Add authentication credentials |
| */ |
| public <T extends Client>T addCredentials( |
| String target, |
| String realm, |
| String scheme, |
| Credentials credentials) |
| throws URISyntaxException { |
| String host = AuthScope.ANY_HOST; |
| int port = AuthScope.ANY_PORT; |
| if (target != null) { |
| URI uri = new URI(target); |
| host = uri.getHost(); |
| port = uri.getPort(); |
| } |
| HttpHost targetHost = new HttpHost(host,port,scheme); |
| getDefaultHttpClient().getCredentialsProvider().setCredentials( |
| new AuthScope( |
| targetHost.getHostName(), |
| targetHost.getPort(), |
| realm != null ? realm : AuthScope.ANY_REALM, |
| scheme != null ? scheme : AuthScope.ANY_SCHEME), |
| credentials); |
| return (T)this; |
| } |
| |
| /** |
| * When multiple authentication schemes are supported by a server, the client will automatically select a scheme |
| * based on the configured priority. For instance, to tell the client to prefer "digest" over "basic", set the |
| * priority by calling setAuthenticationSchemePriority("digest","basic") |
| */ |
| public <T extends Client>T setAuthenticationSchemePriority(String... scheme) { |
| List<?> authPrefs = java.util.Arrays.asList(scheme); |
| client.getParams().setParameter(AuthPNames.TARGET_AUTH_PREF, authPrefs); |
| return (T)this; |
| } |
| |
| /** |
| * Returns the current listing of preferred authentication schemes, in order of preference |
| * |
| * @see setAuthenticationSchemePriority |
| */ |
| public String[] getAuthenticationSchemePriority() { |
| List<?> list = (List<?>) client.getParams().getParameter(AuthPNames.TARGET_AUTH_PREF); |
| return list.toArray(new String[list.size()]); |
| } |
| |
| /** |
| * Set the maximum number of connections allowed for a single host. This |
| * is only effective if the Connection Manager implementation used is |
| * a ThreadSafeClientConnManager, otherwise, an IllegalStateException is |
| * thrown. Subclasses can override this method if a different Client |
| * Connection Manager implementation is used that supports setting the |
| * max connections per host. |
| */ |
| public <T extends Client>T setMaxConnectionsPerHost(int max) { |
| ClientConnectionManager ccm = |
| client.getConnectionManager(); |
| if (ccm instanceof ThreadSafeClientConnManager) { |
| ThreadSafeClientConnManager cm = |
| (ThreadSafeClientConnManager) ccm; |
| cm.setDefaultMaxPerRoute(max); |
| } else { |
| throw new IllegalStateException(); |
| } |
| return (T)this; |
| } |
| |
| /** |
| * Return the maximum number of connections allowed for a single host. This |
| * only returns a value if the Connection Manager implementation is a |
| * ThreadSafeClientConnManager instance, otherwise it returns -1. Subclasses |
| * can override this behavior if a different Connection Manager implementation |
| * is used. |
| */ |
| public int getMaxConnectionsPerHost() { |
| ClientConnectionManager ccm = |
| client.getConnectionManager(); |
| if (ccm instanceof ThreadSafeClientConnManager) { |
| ThreadSafeClientConnManager cm = |
| (ThreadSafeClientConnManager) client.getConnectionManager(); |
| return cm.getDefaultMaxPerRoute(); |
| } else return -1; |
| } |
| |
| /** |
| * Return the maximum number of connections allowed for the client |
| */ |
| public <T extends Client>T setMaxConnectionsTotal(int max) { |
| ClientConnectionManager ccm = |
| client.getConnectionManager(); |
| if (ccm instanceof ThreadSafeClientConnManager) { |
| ThreadSafeClientConnManager cm = |
| (ThreadSafeClientConnManager) client.getConnectionManager(); |
| cm.setMaxTotal(max); |
| } else { |
| throw new IllegalStateException(); |
| } |
| return (T)this; |
| } |
| |
| /** |
| * Return the maximum number of connections allowed for the client |
| */ |
| public int getMaxConnectionsTotal() { |
| ClientConnectionManager ccm = |
| client.getConnectionManager(); |
| if (ccm instanceof ThreadSafeClientConnManager) { |
| ThreadSafeClientConnManager cm = |
| (ThreadSafeClientConnManager) client.getConnectionManager(); |
| return cm.getMaxTotal(); |
| } return -1; |
| } |
| |
| /** |
| * Configure the client to use the specified proxy |
| */ |
| public <T extends Client>T setProxy(String host, int port) { |
| HttpHost proxy = |
| new HttpHost(host, port); |
| client.getParams().setParameter( |
| ConnRoutePNames.DEFAULT_PROXY, proxy); |
| return (T)this; |
| } |
| |
| /** |
| * Configure the client to use the proxy configured for the JVM |
| * @return |
| */ |
| public <T extends Client>T setStandardProxy() { |
| ProxySelectorRoutePlanner routePlanner = |
| new ProxySelectorRoutePlanner( |
| client.getConnectionManager().getSchemeRegistry(), |
| ProxySelector.getDefault()); |
| getDefaultHttpClient().setRoutePlanner(routePlanner); |
| return (T)this; |
| } |
| |
| /** |
| * Get the underlying HTTP Client's Cookie Store, creating it automatically |
| * if it does not already exist |
| */ |
| public CookieStore getCookieStore() { |
| return getCookieStore(true); |
| } |
| |
| /** |
| * Get the underlying HTTP Client's Cookie Store, optionally creating it |
| * if it does not already exist |
| */ |
| public CookieStore getCookieStore(boolean create) { |
| CookieStore store = getDefaultHttpClient().getCookieStore(); |
| if (store == null && create) { |
| synchronized(this) { |
| store = new BasicCookieStore(); |
| getDefaultHttpClient().setCookieStore(store); |
| } |
| } |
| return store; |
| } |
| |
| /** |
| * Set the underlying HTTP Client Cookie Store implementation |
| */ |
| public <T extends Client>T setCookieStore(CookieStore cookieStore) { |
| getDefaultHttpClient().setCookieStore(cookieStore); |
| return (T)this; |
| } |
| |
| /** |
| * Manually add cookies |
| */ |
| public <T extends Client>T addCookie( |
| String domain, |
| String name, |
| String value) { |
| BasicClientCookie cookie = |
| new BasicClientCookie(name,value); |
| cookie.setVersion(0); |
| cookie.setDomain(domain); |
| getCookieStore().addCookie(cookie); |
| return (T)this; |
| } |
| |
| /** |
| * Manually add cookies |
| */ |
| public <T extends Client>T addCookie( |
| String domain, |
| String name, |
| String value, |
| String path, |
| Date expires, |
| boolean secure) { |
| BasicClientCookie cookie = |
| new BasicClientCookie(name,value); |
| cookie.setVersion(0); |
| cookie.setDomain(domain); |
| cookie.setPath(path); |
| cookie.setExpiryDate(expires); |
| cookie.setSecure(secure); |
| getCookieStore().addCookie(cookie); |
| return (T)this; |
| } |
| |
| /** |
| * Manually add cookies |
| */ |
| public <T extends Client>T addCookies(Cookie cookie) { |
| getCookieStore().addCookie(cookie); |
| return (T)this; |
| } |
| |
| /** |
| * Manually add cookies |
| */ |
| public <T extends Client>T addCookies(Cookie... cookies) { |
| for (Cookie cookie : cookies) |
| getCookieStore().addCookie(cookie); |
| return (T)this; |
| } |
| |
| /** |
| * Get all the cookies |
| */ |
| public Iterable<Cookie> getCookies() { |
| return getCookieStore().getCookies(); |
| } |
| |
| /** |
| * Get the cookies for a specific domain and path |
| */ |
| public Iterable<Cookie> getCookies(String domain, String path) { |
| List<Cookie> cookies = getCookieStore().getCookies(); |
| List<Cookie> list = new ArrayList<Cookie>(); |
| for (Cookie cookie : cookies) { |
| String test = cookie.getDomain(); |
| if (test.startsWith(".")) |
| test = test.substring(1); |
| if ((domain.endsWith(test) || test.endsWith(domain)) && |
| (path == null || cookie.getPath().startsWith(path))) { |
| list.add(cookie); |
| } |
| } |
| return list; |
| } |
| |
| /** |
| * Get the cookies for a specific domain |
| */ |
| public Iterable<Cookie> getCookies(String domain) { |
| return getCookies(domain, null); |
| } |
| |
| /** |
| * Clear the cookies |
| */ |
| public <T extends Client>T clearCookies() { |
| getCookieStore().clear(); |
| return (T)this; |
| } |
| |
| public <T extends Client>T clearExpiredCookies() { |
| getCookieStore().clearExpired(new Date()); |
| return (T)this; |
| } |
| |
| /** |
| * Sets the timeout until a connection is etablished. A value of zero means the timeout is not used. The default |
| * value is zero. |
| */ |
| public <T extends Client>T setConnectionTimeout(int timeout) { |
| client.getParams().setIntParameter( |
| CoreConnectionPNames.CONNECTION_TIMEOUT, |
| Math.max(0,timeout)); |
| return (T)this; |
| } |
| |
| /** |
| * Sets the default socket timeout (SO_TIMEOUT) in milliseconds which is the timeout for waiting for data. A timeout |
| * value of zero is interpreted as an infinite timeout. |
| */ |
| public <T extends Client>T setSocketTimeout(int timeout) { |
| client.getParams().setIntParameter( |
| CoreConnectionPNames.SO_TIMEOUT, |
| Math.max(0,timeout)); |
| return (T)this; |
| } |
| |
| /** |
| * Return the timeout until a connection is etablished, in milliseconds. A value of zero means the timeout is not |
| * used. The default value is zero. |
| */ |
| public int getConnectionTimeout() { |
| return client.getParams().getIntParameter( |
| CoreConnectionPNames.CONNECTION_TIMEOUT, 0); |
| } |
| |
| /** |
| * Return the socket timeout for the connection in milliseconds A timeout value of zero is interpreted as an |
| * infinite timeout. |
| */ |
| public int getSocketTimeout() { |
| return client.getParams().getIntParameter( |
| CoreConnectionPNames.SO_TIMEOUT, 0); |
| } |
| |
| /** |
| * Determines whether Nagle's algorithm is to be used. The Nagle's algorithm tries to conserve bandwidth by |
| * minimizing the number of segments that are sent. When applications wish to decrease network latency and increase |
| * performance, they can disable Nagle's algorithm (that is enable TCP_NODELAY). Data will be sent earlier, at the |
| * cost of an increase in bandwidth consumption. |
| */ |
| public void setTcpNoDelay(boolean enable) { |
| client.getParams().setBooleanParameter( |
| CoreConnectionPNames.TCP_NODELAY, enable); |
| } |
| |
| /** |
| * Tests if Nagle's algorithm is to be used. |
| */ |
| public boolean getTcpNoDelay() { |
| return client.getParams().getBooleanParameter( |
| CoreConnectionPNames.TCP_NODELAY, false); |
| } |
| |
| /** |
| * Return the HttpConnectionManagerParams object of the underlying HttpClient. This enables you to configure options |
| * not explicitly exposed by the AbderaClient |
| */ |
| public HttpParams getHttpConnectionManagerParams() { |
| return client.getParams(); |
| } |
| |
| /** |
| * Set the maximum number of redirects |
| */ |
| public <T extends Client>T setMaximumRedirects(int redirects) { |
| client.getParams().setIntParameter( |
| ClientPNames.MAX_REDIRECTS, |
| Math.max(0, redirects)); |
| return (T)this; |
| } |
| |
| /** |
| * Get the maximum number of redirects |
| */ |
| public int getMaximumRedirects() { |
| return client.getParams().getIntParameter( |
| ClientPNames.MAX_REDIRECTS, |
| DEFAULT_MAX_REDIRECTS); |
| } |
| |
| /** |
| * Clear all credentials (including proxy credentials) |
| */ |
| public <T extends Client>T clearCredentials() { |
| getDefaultHttpClient().getCredentialsProvider().clear(); |
| return (T)this; |
| } |
| |
| public <T extends Session>T newSession() { |
| return (T)new Session(this); |
| } |
| |
| public void shutdown() { |
| client.getConnectionManager().shutdown(); |
| } |
| |
| public static String getDefaultUserAgent() { |
| Version version = AnnoUtil.getVersion(BasicClient.class); |
| return String.format("%s/%s",version.name(),version.value()); |
| } |
| } |