view src/org/tmatesoft/hg/internal/remote/RemoteConnectorDescriptor.java @ 699:a483b2b68a2e

Provisional APIs and respective implementation for http, https and ssh remote repositories
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 08 Aug 2013 19:18:50 +0200
parents 822f3a83ff57
children 992fa84e7885
line wrap: on
line source
/*
 * Copyright (c) 2013 TMate Software Ltd
 *  
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * For information on how to redistribute this software under
 * the terms of a license other than GNU General Public License
 * contact TMate Software at support@hg4j.com
 */
package org.tmatesoft.hg.internal.remote;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

import org.tmatesoft.hg.core.HgBadArgumentException;
import org.tmatesoft.hg.core.SessionContext;
import org.tmatesoft.hg.repo.HgRemoteRepository;
import org.tmatesoft.hg.util.Pair;

/**
 * Connector-aware descriptor of remote repository, i.e. descriptor that uses 
 * {@link Connector Connectors} to connect to a remote repository.
 * 
 * <p>Candidate to become public API, with createConnector() method, so that {@link HgRemoteRepository} 
 * may accept instances of that interfact directly
 * 
 * 
 * @author Artem Tikhomirov
 * @author TMate Software Ltd.
 */
public class RemoteConnectorDescriptor implements HgRemoteRepository.RemoteDescriptor {
	
	private Map<String, Pair<ClassLoader, String>> connFactory;
	private final URI uri;

	public RemoteConnectorDescriptor(Map<String,Pair<ClassLoader, String>> scheme2connectorMap, URI uriRemote) {
		this(uriRemote);
		connFactory = scheme2connectorMap;
	}
	
	protected RemoteConnectorDescriptor(URI uriRemote) {
		uri = uriRemote;
	}

	public URI getURI() {
		return uri;
	}

	public Connector createConnector() throws HgBadArgumentException {
		Pair<ClassLoader, String> connectorToBe = connFactory.get(uri.getScheme());
		if (connectorToBe == null || connectorToBe.second() == null) {
			throw new HgBadArgumentException(String.format("Can't instantiate connector for scheme '%s'", uri.getScheme()), null);
		}
		try {
			Class<?> connClass = connectorToBe.first().loadClass(connectorToBe.second());
			if (!Connector.class.isAssignableFrom(connClass)) {
				throw new HgBadArgumentException(String.format("Connector %s for scheme '%s' is not a subclass of %s", connectorToBe.second(), uri.getScheme(), Connector.class.getName()), null);
			}
			final Object connector = connClass.newInstance();
			return Connector.class.cast(connector);
		} catch (ClassNotFoundException ex) {
			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
		} catch (InstantiationException ex) {
			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
		} catch (IllegalAccessException ex) {
			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
		}
	}

	// I don't see a reason to expose provider of RemoteDescriptors yet
	// although it might not be the best idea for session context to serve as provider intermediate
	public static class Provider {
		private final Map<String, Pair<ClassLoader, String>> knownConnectors = new HashMap<String, Pair<ClassLoader, String>>(5);
		
		{
			final ClassLoader cl = Provider.class.getClassLoader();
			knownConnectors.put("http", new Pair<ClassLoader, String>(cl, HttpConnector.class.getName()));
			knownConnectors.put("https", new Pair<ClassLoader, String>(cl, HttpConnector.class.getName()));
			 // FIXME replace SshConnector.class with fqn string to avoid dependency from the trilead library in runtime
			knownConnectors.put("ssh", new Pair<ClassLoader, String>(cl, SshConnector.class.getName()));
		}

		public HgRemoteRepository.RemoteDescriptor get(SessionContext ctx, URI uri) {
			if (knownConnectors.containsKey(uri.getScheme())) {
				return new RemoteConnectorDescriptor(knownConnectors, uri);
			}
			return null;
		}
	}
}