com.pastdev.jsch.tunnel.TunnelConnectionManager Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsch-extension Show documentation
Show all versions of jsch-extension Show documentation
A set of extensions on top of JSch providing a full SCP protocol implementation, tunneling including multi-hop, a Java 7 FileSystem like implementation for Java 6 and remote command execution
The newest version!
package com.pastdev.jsch.tunnel;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Pattern;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.jcraft.jsch.JSchException;
import com.pastdev.jsch.IOUtils;
import com.pastdev.jsch.SessionFactory;
import com.pastdev.jsch.SessionFactory.SessionFactoryBuilder;
import com.pastdev.jsch.proxy.SshProxy;
/**
* Manages a collection of tunnels. This implementation will:
*
* - Ensure a minimum number of ssh connections are made
* - Ensure all connections are open/closed at the same time
* - Provide a convenient syntax for defining tunnels
*
*/
public class TunnelConnectionManager implements Closeable {
private static final Pattern PATTERN_TUNNELS_CFG_COMMENT_LINE = Pattern.compile( "^\\s*(?:#.*)?$" );
private static Logger logger = LoggerFactory.getLogger( TunnelConnectionManager.class );
private SessionFactory baseSessionFactory;
private List tunnelConnections;
/**
* Creates a TunnelConnectionManager that will use the
* baseSessionFactory
to obtain its session connections.
* Because this constructor does not set the tunnel connections for you, you
* will need to call {@link #setTunnelConnections(Iterable)}.
*
* @param baseSessionFactory
* The session factory
* @throws JSchException
* For connection failures
*
* @see #setTunnelConnections(Iterable)
*/
public TunnelConnectionManager( SessionFactory baseSessionFactory ) throws JSchException {
logger.debug( "Creating TunnelConnectionManager" );
this.baseSessionFactory = baseSessionFactory;
}
/**
* Creates a TunnelConnectionManager that will use the
* baseSessionFactory
to obtain its session connections and
* provide the tunnels specified.
*
* @param baseSessionFactory
* The session factory
* @param pathAndSpecList
* A list of {@link #setTunnelConnections(Iterable) path and
* spec} strings
* @throws JSchException
* For connection failures
*
* @see #setTunnelConnections(Iterable)
*/
public TunnelConnectionManager( SessionFactory baseSessionFactory, String... pathAndSpecList ) throws JSchException {
this( baseSessionFactory, Arrays.asList( pathAndSpecList ) );
}
/**
* Creates a TunnelConnectionManager that will use the
* baseSessionFactory
to obtain its session connections and
* provide the tunnels specified.
*
* @param baseSessionFactory
* The session factory
* @param pathAndSpecList
* A list of {@link #setTunnelConnections(Iterable) path and
* spec} strings
* @throws JSchException
* For connection failures
*
* @see #setTunnelConnections(Iterable)
*/
public TunnelConnectionManager( SessionFactory baseSessionFactory, Iterable pathAndSpecList ) throws JSchException {
this( baseSessionFactory );
setTunnelConnections( pathAndSpecList );
}
/**
* Closes all sessions and their associated tunnels.
*
* @see com.pastdev.jsch.tunnel.TunnelConnection#close()
*/
@Override
public void close() {
for ( TunnelConnection tunnelConnection : tunnelConnections ) {
IOUtils.closeAndLogException( tunnelConnection );
}
}
/**
* Will re-open any connections that are not still open.
*
* @throws JSchException
* For connection failures
*/
public void ensureOpen() throws JSchException {
for ( TunnelConnection tunnelConnection : tunnelConnections ) {
if ( !tunnelConnection.isOpen() ) {
tunnelConnection.reopen();
}
}
}
/**
* Returns the tunnel matching the supplied values, or null
if
* there isn't one that matches.
*
* @param destinationHostname
* The tunnels destination hostname
* @param destinationPort
* The tunnels destination port
*
* @return The tunnel matching the supplied values
*
* @see com.pastdev.jsch.tunnel.TunnelConnection#getTunnel(String, int)
*/
public Tunnel getTunnel( String destinationHostname, int destinationPort ) {
// might be better to cache, but dont anticipate massive numbers
// of tunnel connections...
for ( TunnelConnection tunnelConnection : tunnelConnections ) {
Tunnel tunnel = tunnelConnection.getTunnel(
destinationHostname, destinationPort );
if ( tunnel != null ) {
return tunnel;
}
}
return null;
}
/**
* Opens all the necessary sessions and connects all of the tunnels.
*
* @throws JSchException
* For connection failures
*
* @see com.pastdev.jsch.tunnel.TunnelConnection#open()
*/
public void open() throws JSchException {
for ( TunnelConnection tunnelConnection : tunnelConnections ) {
tunnelConnection.open();
}
}
/**
* Creates a set of tunnel connections based upon the contents of
* tunnelsConfig
. The format of this file is one path and
* tunnel per line. Comments and empty lines are allowed and are excluded
* using the pattern ^\s*(?:#.*)?$
.
*
* @param tunnelsConfig A file containing tunnel configuration
* @throws IOException
* If unable to read from tunnelsConfig
* @throws JSchException
* For connection failures
*/
public void setTunnelConnectionsFromFile( File tunnelsConfig ) throws IOException, JSchException {
List pathAndTunnels = new ArrayList();
BufferedReader reader = null;
try {
reader = new BufferedReader( new FileReader( tunnelsConfig ) );
String line = null;
while ( (line = reader.readLine()) != null ) {
if ( PATTERN_TUNNELS_CFG_COMMENT_LINE.matcher( line ).matches() ) {
continue;
}
pathAndTunnels.add( line );
}
}
finally {
if ( reader != null ) {
IOUtils.closeAndLogException( reader );
}
}
setTunnelConnections( pathAndTunnels );
}
/**
* Creates a set of tunnel connections based upon the pathAndTunnels. Each
* entry of pathAndTunnels must be of the form (in EBNF):
*
*
* path and tunnels = path and tunnel, {new line, path and tunnel}
* path and tunnel = path, "|", tunnel
* new line = "\n"
* path = path part, {"->", path part}
* path part = {user, "@"}, hostname
* tunnel = {local part}, ":", destination hostname, ":", destination port
* local part = {local alias, ":"}, local port
* local alias = hostname
* local port = port
* destination hostname = hostname
* destination port = port
* user = ? user name ?
* hostname = ? hostname ?
* port = ? port ?
*
*
*
* For example:
*
*
*
* [email protected]>[email protected]|drteeth:8080:drteeth.muppets.com:80
*
*
*
* Says open an ssh connection as user jimhenson
to host
* admin.muppets.com
. Then, through that connection, open a
* connection as user animal
to host
* drteethandtheelectricmahem.muppets.com
. Then map local port
* 8080
on the interface with alias drteeth
* through the two-hop tunnel to port 80
on
* drteeth.muppets.com
.
*
*
* @param pathAndSpecList
* A list of path and spec entries
*
* @throws JSchException
* For connection failures
*/
public void setTunnelConnections( Iterable pathAndSpecList ) throws JSchException {
Map> tunnelMap = new HashMap>();
for ( String pathAndSpecString : pathAndSpecList ) {
String[] pathAndSpec = pathAndSpecString.trim().split( "\\|" );
Set tunnelList = tunnelMap.get( pathAndSpec[0] );
if ( tunnelList == null ) {
tunnelList = new HashSet();
tunnelMap.put( pathAndSpec[0], tunnelList );
}
tunnelList.add( new Tunnel( pathAndSpec[1] ) );
}
tunnelConnections = new ArrayList();
SessionFactoryCache sessionFactoryCache = new SessionFactoryCache( baseSessionFactory );
for ( String path : tunnelMap.keySet() ) {
tunnelConnections.add( new TunnelConnection( sessionFactoryCache.getSessionFactory( path ),
new ArrayList( tunnelMap.get( path ) ) ) );
}
}
/*
* Used to ensure duplicate paths are not created which will minimize the
* number of connections needed.
*/
static class SessionFactoryCache {
private Map sessionFactoryByPath;
private SessionFactory defaultSessionFactory;
SessionFactoryCache( SessionFactory baseSessionFactory ) {
this.defaultSessionFactory = baseSessionFactory;
this.sessionFactoryByPath = new HashMap();
}
public SessionFactory getSessionFactory( String path ) throws JSchException {
SessionFactory sessionFactory = null;
String key = null;
for ( String part : path.split( "\\-\\>" ) ) {
if ( key == null ) {
key = part;
}
else {
key += "->" + part;
}
if ( sessionFactoryByPath.containsKey( key ) ) {
sessionFactory = sessionFactoryByPath.get( key );
continue;
}
SessionFactoryBuilder builder = null;
if ( sessionFactory == null ) {
builder = defaultSessionFactory.newSessionFactoryBuilder();
}
else {
builder = sessionFactory.newSessionFactoryBuilder()
.setProxy( new SshProxy( sessionFactory ) );
}
// start with [username@]hostname[:port]
String[] userAtHost = part.split( "\\@" );
String hostname = null;
if ( userAtHost.length == 2 ) {
builder.setUsername( userAtHost[0] );
hostname = userAtHost[1];
}
else {
hostname = userAtHost[0];
}
// left with hostname[:port]
String[] hostColonPort = hostname.split( "\\:" );
builder.setHostname( hostColonPort[0] );
if ( hostColonPort.length == 2 ) {
builder.setPort( Integer.parseInt( hostColonPort[1] ) );
}
sessionFactory = builder.build();
}
return sessionFactory;
}
}
}