Jump to: navigation, search

Mylyn/Architecture/Web

The org.eclipse.mylyn.web.core plug-in provides helper classes for commonly used network operations that are executed by Mylyn connectors. It has utility classes for Apache HttpClient which is used by the connector reference implementations for processing HTTP requests.

The org.eclipse.mylyn.web.core plug-in can be used in standalone applications and is not coupled to other parts of the Mylyn framework.

Password Prompting

If authentication is required during access of a network resource a callback to the UI to request credentials from the user is required. The AbstractWebLocation class which is used in the web core plug-in to represent a URL provides the requestCredentials(AuthenticationType, String, IProgressMonitor) method for that purpose. AuthenticationType is an enum that specifies the resource to authenticate against, e.g. repository, proxy or http server.

The credentials can be retrieved by invoking getCredentials(AuthenticationType type) which returns an object of type AuthenticationCredentials. Currently only username/password credentials are supported:

  • Username, Password, (Realm) [HTML Form, HTTP Basic/Digest/NTLM]
  • (SSL Certificate)
boolean authenticated = false;
while (!authenticated) {
	AuthenticationCredentials credentials = location.getCredentials(AuthenticationType.REPOSITORY);
 
	AuthScope authScope = new AuthScope(AuthScope.ANY_HOST, AuthScope.ANY_PORT, AuthScope.ANY_REALM);
	httpClient.getState().setCredentials(authScope,
		WebUtil.getHttpClientCredentials(credentials, WebUtil.getHost(repositoryUrl)));
 
	GetMethod method = new GetMethod(WebUtil.getRequestPath(repositoryUrl + LOGIN_URL));
	int code;
	try {
		code = WebUtil.execute(httpClient, hostConfiguration, method, monitor);
 
		AuthenticationType authenticationType = null;
		if (code == HttpStatus.SC_UNAUTHORIZED || code == HttpStatus.SC_FORBIDDEN) {
			authenticationType = AuthenticationType.REPOSITORY;
		} else if (code == HttpStatus.SC_PROXY_AUTHENTICATION_REQUIRED) {
			authenticationType = AuthenticationType.PROXY;
		} else {
			authenticated = true;
		}
 
		if (!authenticated) {
			try {
				location.requestCredentials(authenticationType, null, monitor);
			} catch (UnsupportedRequestException e) {
				throw new TracLoginException();
			}
 
			hostConfiguration = WebUtil.createHostConfiguration(httpClient, location, monitor);
		}
	} finally {
		method.releaseConnection();
	}
}

The Tasks UI plug-in provides the class TaskRepositoryLocationUi which supports password management with TaskRepository objects.

Proxy Support

  • HTTP/HTTPS Proxy
  • Socks Proxy

Cancellation

The org.eclipse.mylyn.web.core plug-in helper classes for sending HTTP requests that can be cancelled by a user through the IProgressMonitor interface. The basic principle is that all network I/O, i.e. opening of sockets, reading and writing of streams, is done on separate threads while the IProgressMonitor is polled for its cancelled state. If a cancellation is detected the network operation is aborted by closing the socket or stream.

The web core plug-in maintains a thread pool that is shared by all plug-ins using it's API.

Apache HttpClient

Below is an source of the WebUtil.getTitleFromUrl() method that demonstrates how to send a GET request and parse the response while providing cancellation support.

In order to provide cancellation while a connection is established WebUtil.createHostConfiguration(HttpClient, AbstractWebLocation, IProgressMonitor) should be used to create a host configuration. The returned host configuration uses a custom socket factory that provides cancellation support while a socket is being connected.

Requests should be executed through WebUtil.execute(HttpClient, HostConfiguration, HttpMethod, IProgressMonitor). This method will invoke HttpClient.executeMethod() and provide cancellation support while the HTTP request is sent and the response is parsed.

For reading the response WebUtil.getResponseBodyAsStream(HttpMethodBase, IProgressMonitor) should be used which returns an PollingInputStream that wraps the input stream that is provided by HttpClient and supports cancellation. It is very important to always close the returned input stream in a finally block. If the stream is not closed it will leave a zombie thread which will eventually exhaust the thread pool.

public static String getTitleFromUrl(AbstractWebLocation location, IProgressMonitor monitor) throws IOException, ParseException {
	monitor = Policy.monitorFor(monitor);
	try {
		monitor.beginTask("Retrieving " + location.getUrl(), IProgressMonitor.UNKNOWN);
 
		HttpClient client = new HttpClient();
		WebUtil.configureHttpClient(client, "");
 
		GetMethod method = new GetMethod(location.getUrl());
		try {
			HostConfiguration hostConfiguration = WebUtil.createHostConfiguration(client, location, monitor);
			int result = WebUtil.execute(client, hostConfiguration, method, monitor);
			if (result == HttpStatus.SC_OK) {
				InputStream in = WebUtil.getResponseBodyAsStream(method, monitor);
				try {
					BufferedReader reader = new BufferedReader(new InputStreamReader(in));
					HtmlStreamTokenizer tokenizer = new HtmlStreamTokenizer(reader, null);
					for (Token token = tokenizer.nextToken(); token.getType() != Token.EOF; token = okenizer.nextToken()) {
						if (token.getType() == Token.TAG) {
							HtmlTag tag = (HtmlTag) token.getValue();
							if (tag.getTagType() == Tag.TITLE) {
								return getText(tokenizer);
							}
						}
					}
				} finally {
					in.close();
				}
			}
		} finally {
			method.releaseConnection();
		}
	} finally {
		monitor.done();
	}
	return null;
}

Apache Axis

The JIRA connector uses a customized transport that uses a thread-local variable to keep track of the progress monitor for a particular request. The implementation can be found in JiraHttpSender and JiraRequest.