/******************************************************************************* * Copyright (c) 2010 Steffen Pingel and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Steffen Pingel - initial API and implementation *******************************************************************************/ package org.eclipse.mylyn.internal.commons.xmlrpc; import java.net.MalformedURLException; import java.net.URL; import java.util.TimeZone; import org.apache.commons.httpclient.Credentials; import org.apache.commons.httpclient.Header; import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.HttpMethod; import org.apache.commons.httpclient.auth.AuthScheme; import org.apache.commons.httpclient.auth.AuthScope; import org.apache.commons.httpclient.auth.DigestScheme; import org.apache.commons.httpclient.cookie.CookiePolicy; import org.apache.xmlrpc.XmlRpcException; import org.apache.xmlrpc.client.XmlRpcClient; import org.apache.xmlrpc.client.XmlRpcClientConfigImpl; import org.apache.xmlrpc.serializer.CharSetXmlWriterFactory; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Platform; import org.eclipse.mylyn.commons.net.AbstractWebLocation; import org.eclipse.mylyn.commons.net.AuthenticationCredentials; import org.eclipse.mylyn.commons.net.AuthenticationType; import org.eclipse.mylyn.commons.net.WebUtil; import org.eclipse.osgi.util.NLS; /** * Facilitates connections to repositories accessed through XML-RPC. * * @author Steffen Pingel */ public class CommonXmlRpcClient { static final boolean DEBUG_AUTH = Boolean.valueOf(Platform.getDebugOption("org.eclipse.mylyn.commons.xmlrpc/debug/authentication")); //$NON-NLS-1$ static final boolean DEBUG_XMLRPC = Boolean.valueOf(Platform.getDebugOption("org.eclipse.mylyn.commons.xmlrpc/debug/xmlrpc")); //$NON-NLS-1$ private static final String DEFAULT_CHARSET = "UTF-8"; //$NON-NLS-1$ private static final String DEFAULT_TIME_ZONE = TimeZone.getDefault().getID(); private static final String DEFAULT_USER_AGENT = "Apache XML-RPC/3.0"; //$NON-NLS-1$ private static final String DEFAULT_CONTENT_TYPE = "text/xml"; //$NON-NLS-1$ private static HttpClient createHttpClient(String userAgent) { HttpClient httpClient = new HttpClient(); httpClient.setHttpConnectionManager(WebUtil.getConnectionManager()); httpClient.getParams().setCookiePolicy(CookiePolicy.RFC_2109); WebUtil.configureHttpClient(httpClient, userAgent); return httpClient; } private final AuthScope authScope; private XmlRpcClientConfigImpl config; volatile DigestScheme digestScheme; private HttpClientTransportFactory factory; final HttpClient httpClient; private final AbstractWebLocation location; // private boolean probed; private XmlRpcClient xmlrpc; private volatile boolean contentTypeCheckingEnabled; public CommonXmlRpcClient(AbstractWebLocation location) { this(location, createHttpClient(DEFAULT_USER_AGENT)); } public CommonXmlRpcClient(AbstractWebLocation location, HttpClient client) { this.location = location; this.httpClient = createHttpClient(DEFAULT_USER_AGENT); this.authScope = new AuthScope(WebUtil.getHost(location.getUrl()), WebUtil.getPort(location.getUrl()), null, AuthScope.ANY_SCHEME); } public <T> T call(final IProgressMonitor monitor, final String method, final Object... parameters) throws XmlRpcException { return new XmlRpcOperation<T>(this) { @SuppressWarnings("unchecked") @Override public T execute() throws XmlRpcException { return (T) call(monitor, method, parameters); } }.execute(); } public MulticallResult call(final IProgressMonitor monitor, final Multicall call) throws XmlRpcException { return new XmlRpcOperation<MulticallResult>(this) { @Override public MulticallResult execute() throws XmlRpcException { return call(monitor, call); } }.execute(); } protected void createXmlRpcClient() { config = new XmlRpcClientConfigImpl(); config.setEncoding(DEFAULT_CHARSET); config.setTimeZone(TimeZone.getTimeZone(DEFAULT_TIME_ZONE)); config.setContentLengthOptional(false); config.setConnectionTimeout(WebUtil.getConnectionTimeout()); config.setReplyTimeout(WebUtil.getSocketTimeout()); xmlrpc = new XmlRpcClient(); xmlrpc.setConfig(config); // bug 307200: force factory that supports proper UTF-8 encoding xmlrpc.setXmlWriterFactory(new CharSetXmlWriterFactory()); factory = new HttpClientTransportFactory(xmlrpc, httpClient); factory.setLocation(location); factory.setInterceptor(new HttpMethodInterceptor() { public void processRequest(HttpMethod method) { DigestScheme scheme = digestScheme; if (scheme != null) { if (DEBUG_AUTH) { System.err.println(location.getUrl() + ": Digest scheme is present"); //$NON-NLS-1$ } Credentials creds = httpClient.getState().getCredentials(authScope); if (creds != null) { if (DEBUG_AUTH) { System.err.println(location.getUrl() + ": Setting digest scheme for request"); //$NON-NLS-1$ } method.getHostAuthState().setAuthScheme(digestScheme); method.getHostAuthState().setAuthRequested(true); } } } @SuppressWarnings("null") public void processResponse(HttpMethod method) throws XmlRpcException { if (isContentTypeCheckingEnabled()) { Header contentTypeHeader = method.getResponseHeader("Content-Type"); //$NON-NLS-1$ if (contentTypeHeader == null || !DEFAULT_CONTENT_TYPE.equals(contentTypeHeader.getValue())) { throw new XmlRpcIllegalContentTypeException( NLS.bind( "The server returned an unexpected content type: ''{0}''", contentTypeHeader.getValue()), contentTypeHeader.getValue()); //$NON-NLS-1$ } } AuthScheme authScheme = method.getHostAuthState().getAuthScheme(); if (authScheme instanceof DigestScheme) { digestScheme = (DigestScheme) authScheme; if (DEBUG_AUTH) { System.err.println(location.getUrl() + ": Received digest scheme"); //$NON-NLS-1$ } } } }); xmlrpc.setTransportFactory(factory); try { config.setServerURL(new URL(location.getUrl())); } catch (MalformedURLException e) { throw new RuntimeException(e); } } public synchronized XmlRpcClient getClient() { if (xmlrpc == null) { createXmlRpcClient(); } return xmlrpc; } public HttpClient getHttpClient() { return httpClient; } public AbstractWebLocation getLocation() { return location; } // public boolean isProbed() { // return probed; // } // // public void setProbed(boolean probed) { // this.probed = probed; // } AuthenticationCredentials updateCredentials() { // update configuration with latest values AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY); if (credentials != null) { Credentials httpCredentials = WebUtil.getHttpClientCredentials(credentials, WebUtil.getHost(location.getUrl())); httpClient.getState().setCredentials(authScope, httpCredentials); // if (CoreUtil.TEST_MODE) { // System.err.println(" Setting credentials: " + httpCredentials); //$NON-NLS-1$ // } httpClient.getState().setCredentials(authScope, httpCredentials); } else { httpClient.getState().clearCredentials(); } return credentials; } public boolean isContentTypeCheckingEnabled() { return contentTypeCheckingEnabled; } public void setContentTypeCheckingEnabled(boolean contentTypeCheckingEnabled) { this.contentTypeCheckingEnabled = contentTypeCheckingEnabled; } }