tikhomirov@698: /*
tikhomirov@698:  * Copyright (c) 2013 TMate Software Ltd
tikhomirov@698:  *  
tikhomirov@698:  * This program is free software; you can redistribute it and/or modify
tikhomirov@698:  * it under the terms of the GNU General Public License as published by
tikhomirov@698:  * the Free Software Foundation; version 2 of the License.
tikhomirov@698:  *
tikhomirov@698:  * This program is distributed in the hope that it will be useful,
tikhomirov@698:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
tikhomirov@698:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
tikhomirov@698:  * GNU General Public License for more details.
tikhomirov@698:  *
tikhomirov@698:  * For information on how to redistribute this software under
tikhomirov@698:  * the terms of a license other than GNU General Public License
tikhomirov@698:  * contact TMate Software at support@hg4j.com
tikhomirov@698:  */
tikhomirov@698: package org.tmatesoft.hg.internal.remote;
tikhomirov@698: 
tikhomirov@698: import java.net.URI;
tikhomirov@698: import java.util.HashMap;
tikhomirov@698: import java.util.Map;
tikhomirov@698: 
tikhomirov@698: import org.tmatesoft.hg.core.HgBadArgumentException;
tikhomirov@698: import org.tmatesoft.hg.core.SessionContext;
tikhomirov@698: import org.tmatesoft.hg.repo.HgLookup.Authenticator;
tikhomirov@698: import org.tmatesoft.hg.repo.HgLookup.RemoteDescriptor;
tikhomirov@698: import org.tmatesoft.hg.repo.HgRemoteRepository;
tikhomirov@698: import org.tmatesoft.hg.util.Pair;
tikhomirov@698: 
tikhomirov@698: /**
tikhomirov@698:  * Connector-aware descriptor of remote repository, i.e. descriptor that uses 
tikhomirov@698:  * {@link Connector Connectors} to connect to a remote repository.
tikhomirov@698:  * 
tikhomirov@698:  * 
Candidate to become public API, with createConnector() method, so that {@link HgRemoteRepository} 
tikhomirov@698:  * may accept instances of that interfact directly
tikhomirov@698:  * 
tikhomirov@698:  * 
tikhomirov@698:  * @author Artem Tikhomirov
tikhomirov@698:  * @author TMate Software Ltd.
tikhomirov@698:  */
tikhomirov@698: public class RemoteConnectorDescriptor implements RemoteDescriptor {
tikhomirov@698: 	
tikhomirov@698: 	private Map> connFactory;
tikhomirov@698: 	private final URI uri;
tikhomirov@698: 
tikhomirov@698: 	public RemoteConnectorDescriptor(Map> scheme2connectorMap, URI uriRemote) {
tikhomirov@698: 		this(uriRemote);
tikhomirov@698: 		connFactory = scheme2connectorMap;
tikhomirov@698: 	}
tikhomirov@698: 	
tikhomirov@698: 	protected RemoteConnectorDescriptor(URI uriRemote) {
tikhomirov@698: 		uri = uriRemote;
tikhomirov@698: 	}
tikhomirov@698: 
tikhomirov@698: 	public URI getURI() {
tikhomirov@698: 		return uri;
tikhomirov@698: 	}
tikhomirov@698: 
tikhomirov@698: 	public Authenticator getAuth() {
tikhomirov@698: 		// TODO Auto-generated method stub
tikhomirov@698: 		return null;
tikhomirov@698: 	}
tikhomirov@698: 
tikhomirov@698: 	public Connector createConnector() throws HgBadArgumentException {
tikhomirov@698: 		Pair connectorToBe = connFactory.get(uri.getScheme());
tikhomirov@698: 		if (connectorToBe == null || connectorToBe.second() == null) {
tikhomirov@698: 			throw new HgBadArgumentException(String.format("Can't instantiate connector for scheme '%s'", uri.getScheme()), null);
tikhomirov@698: 		}
tikhomirov@698: 		try {
tikhomirov@698: 			Class> connClass = connectorToBe.first().loadClass(connectorToBe.second());
tikhomirov@698: 			if (!Connector.class.isAssignableFrom(connClass)) {
tikhomirov@698: 				throw new HgBadArgumentException(String.format("Connector %s for scheme '%s' is not a subclass of %s", connectorToBe.second(), uri.getScheme(), Connector.class.getName()), null);
tikhomirov@698: 			}
tikhomirov@698: 			final Object connector = connClass.newInstance();
tikhomirov@698: 			return Connector.class.cast(connector);
tikhomirov@698: 		} catch (ClassNotFoundException ex) {
tikhomirov@698: 			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
tikhomirov@698: 		} catch (InstantiationException ex) {
tikhomirov@698: 			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
tikhomirov@698: 		} catch (IllegalAccessException ex) {
tikhomirov@698: 			throw new HgBadArgumentException(String.format("Can't instantiate connector %s for scheme '%s'", connectorToBe.second(), uri.getScheme()), ex);
tikhomirov@698: 		}
tikhomirov@698: 	}
tikhomirov@698: 
tikhomirov@698: 	// I don't see a reason to expose provider of RemoteDescriptors yet
tikhomirov@698: 	// although it might not be the best idea for session context to serve as provider intermediate
tikhomirov@698: 	public static class Provider {
tikhomirov@698: 		private final Map> knownConnectors = new HashMap>(5);
tikhomirov@698: 		
tikhomirov@698: 		{
tikhomirov@698: 			final ClassLoader cl = Provider.class.getClassLoader();
tikhomirov@698: 			knownConnectors.put("http", new Pair(cl, HttpConnector.class.getName()));
tikhomirov@698: 			knownConnectors.put("https", new Pair(cl, HttpConnector.class.getName()));
tikhomirov@698: 			 // FIXME replace SshConnector.class with fqn string to avoid dependency from the trilead library in runtime
tikhomirov@698: 			knownConnectors.put("ssh", new Pair(cl, SshConnector.class.getName()));
tikhomirov@698: 		}
tikhomirov@698: 
tikhomirov@698: 		public RemoteDescriptor get(SessionContext ctx, URI uri) {
tikhomirov@698: 			if (knownConnectors.containsKey(uri.getScheme())) {
tikhomirov@698: 				return new RemoteConnectorDescriptor(knownConnectors, uri);
tikhomirov@698: 			}
tikhomirov@698: 			return null;
tikhomirov@698: 		}
tikhomirov@698: 	}
tikhomirov@698: }