/**
* See the NOTICE file distributed with this work
* for additional information regarding copyright ownership.
* Board of Regents of the University of Wisconsin System
* 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 com.microsoft.exchange.impl.http;
import java.net.URI;
import org.apache.http.auth.AuthScheme;
import org.apache.http.auth.AuthScope;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.protocol.ClientContext;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.auth.DigestScheme;
import org.apache.http.impl.conn.PoolingClientConnectionManager;
import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
import org.apache.http.pool.ConnPoolControl;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.springframework.ws.transport.http.HttpComponentsMessageSender;
import com.microsoft.exchange.impl.ExchangeOnlineThrottlingPolicy;
/**
* @author Nicholas Blair
*/
public class CustomHttpComponentsMessageSender extends
HttpComponentsMessageSender {
private boolean preemptiveAuthEnabled = false;
private boolean ntlmAuthEnabled = false;
private AuthScope preemptiveAuthScope = AuthScope.ANY;
private CredentialsProviderFactory credentialsProviderFactory = new ThreadLocalCredentialsProviderFactory();
// preemptiveAuthScheme set by #afterPropertiesSet if enabled
private AuthScheme preemptiveAuthScheme;
private Integer defaultMaxPerRouteOverride;
/**
* @return the defaultMaxPerRouteOverride
*/
public Integer getDefaultMaxPerRouteOverride() {
return defaultMaxPerRouteOverride;
}
/**
* @param defaultMaxPerRouteOverride the defaultMaxPerRouteOverride to set
*/
public void setDefaultMaxPerRouteOverride(Integer defaultMaxPerRouteOverride) {
this.defaultMaxPerRouteOverride = defaultMaxPerRouteOverride;
}
/**
* @return the preemptiveAuthEnabled
*/
public boolean isPreemptiveAuthEnabled() {
return preemptiveAuthEnabled;
}
/**
* @param preemptiveAuthEnabled the preemptiveAuthEnabled to set
*/
public void setPreemptiveAuthEnabled(boolean preemptiveAuthEnabled) {
this.preemptiveAuthEnabled = preemptiveAuthEnabled;
}
/**
* @return the preemptiveAuthScope
*/
public AuthScope getPreemptiveAuthScope() {
return preemptiveAuthScope;
}
/**
* @param preemptiveAuthScope the preemptiveAuthScope to set
*/
public void setPreemptiveAuthScope(AuthScope preemptiveAuthScope) {
this.preemptiveAuthScope = preemptiveAuthScope;
}
/**
* @return the credentialsProviderFactory
*/
public CredentialsProviderFactory getCredentialsProviderFactory() {
return credentialsProviderFactory;
}
/**
* @param credentialsProviderFactory the credentialsProviderFactory to set
*/
public void setCredentialsProviderFactory(
CredentialsProviderFactory credentialsProviderFactory) {
this.credentialsProviderFactory = credentialsProviderFactory;
}
/* (non-Javadoc)
* @see org.springframework.ws.transport.http.HttpComponentsMessageSender#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
super.afterPropertiesSet();
if(isPreemptiveAuthEnabled()) {
this.preemptiveAuthScheme = identifyScheme(getPreemptiveAuthScope().getScheme());
}
boolean overrideSuccess = false;
if(defaultMaxPerRouteOverride != null) {
overrideSuccess = this.overrideDefaultMaxPerRoute(defaultMaxPerRouteOverride);
}
if(overrideSuccess && defaultMaxPerRouteOverride > ExchangeOnlineThrottlingPolicy.MAX_CONCURRENT_CONNECTIONS_IMPERSONATION) {
logger.info("defaultMaxPerRoute is being set to " + defaultMaxPerRouteOverride + " which is in excess of ExchangeOnline's default throttling policy. You may experience regular failed requests when the limit for concurrent connections (" + ExchangeOnlineThrottlingPolicy.MAX_CONCURRENT_CONNECTIONS_IMPERSONATION + " for impersonation) is exceeded ");
}
}
protected Integer getMaxTotalConnections() {
ClientConnectionManager connectionManager = getHttpClient().getConnectionManager();
if (connectionManager instanceof ThreadSafeClientConnManager) {
return ((ThreadSafeClientConnManager) connectionManager).getMaxTotal();
}else if(connectionManager instanceof PoolingClientConnectionManager){
PoolingClientConnectionManager p = (PoolingClientConnectionManager) connectionManager;
return p.getMaxTotal();
}
return null;
}
/**
* Method to expose {@link ThreadSafeClientConnManager#setDefaultMaxPerRoute(int)}
* Only sets the value if the {@link #getHttpClient()} is configured with a {@link ThreadSafeClientConnManager}.
*
* @param defaultMaxPerRoute
* @return true if the property was set
*/
protected boolean overrideDefaultMaxPerRoute(int defaultMaxPerRoute) {
ClientConnectionManager connectionManager = getHttpClient().getConnectionManager();
if(connectionManager instanceof ThreadSafeClientConnManager) {
((ThreadSafeClientConnManager) connectionManager).setDefaultMaxPerRoute(defaultMaxPerRoute);
return true;
}else if(connectionManager instanceof PoolingClientConnectionManager){
PoolingClientConnectionManager p = (PoolingClientConnectionManager) connectionManager;
p.setDefaultMaxPerRoute(defaultMaxPerRoute);
return true;
} else {
logger.error("ignoring call to overrideDefaultMaxPerRoute as existing ClientConnectionManager is not an instance of ThreadSafeClientConnManager: " + connectionManager.getClass());
return false;
}
}
/**
*
* @param scheme
* @return
*/
protected AuthScheme identifyScheme(String scheme) {
if(new BasicScheme().getSchemeName().equalsIgnoreCase(scheme)) {
return new BasicScheme();
} else if (new DigestScheme().getSchemeName().equalsIgnoreCase(scheme)) {
return new DigestScheme();
} else {
// fallback
return new BasicScheme();
}
}
/* (non-Javadoc)
* @see org.springframework.ws.transport.http.HttpComponentsMessageSender#createContext(java.net.URI)
*/
@Override
protected HttpContext createContext(URI uri) {
if(isPreemptiveAuthEnabled()) {
HttpContext context = new BasicHttpContext();
if(preemptiveAuthScheme == null) {
throw new IllegalStateException("preemptiveAuth is enabled, but the preemptiveAuthScheme is null. Was afterPropertiesSet invoked?");
}
context.setAttribute(PreemptiveAuthInterceptor.PREEMPTIVE_AUTH, preemptiveAuthScheme);
CredentialsProvider credentialsProvider = getCredentialsProviderFactory().getCredentialsProvider(uri);
context.setAttribute(ClientContext.CREDS_PROVIDER, credentialsProvider);
return context;
} else {
return super.createContext(uri);
}
}
public boolean isNtlmAuthEnabled() {
return ntlmAuthEnabled;
}
public void setNtlmAuthEnabled(boolean ntlmAuthEnabled) {
this.ntlmAuthEnabled = ntlmAuthEnabled;
}
}