package org.openslx.imagemaster.serverconnection;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.KeyStore;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import org.apache.log4j.Logger;
import org.openslx.bwlp.thrift.iface.ImagePublishData;
import org.openslx.bwlp.thrift.iface.InvocationError;
import org.openslx.bwlp.thrift.iface.TInvocationException;
import org.openslx.bwlp.thrift.iface.TTransferRejectedException;
import org.openslx.bwlp.thrift.iface.TransferInformation;
import org.openslx.filetransfer.Downloader;
import org.openslx.filetransfer.IncomingEvent;
import org.openslx.filetransfer.Listener;
import org.openslx.filetransfer.Uploader;
import org.openslx.imagemaster.Globals;
import org.openslx.imagemaster.crcchecker.CrcFile;
import org.openslx.imagemaster.db.mappers.DbOsVirt;
import org.openslx.imagemaster.db.mappers.DbUser;
import org.openslx.imagemaster.util.RandomString;
import org.openslx.imagemaster.util.Util;
/**
* Class to handle all incoming and outgoing connections.
* Also handles the authentication and the saving/delivering of images.
*/
public class ConnectionHandler implements IncomingEvent
{
private static Logger log = Logger.getLogger( ConnectionHandler.class );
private static SSLContext sslContext;
private static Map<String, AbstractTransfer> pendingIncomingUploads = new ConcurrentHashMap<>();
private static Map<String, AbstractTransfer> pendingIncomingDownloads = new ConcurrentHashMap<>();
private static IncomingEvent eventHandler = new ConnectionHandler();
private static ThreadPoolExecutor uploadPool = new ThreadPoolExecutor( 0, 5, 6, TimeUnit.MINUTES, new SynchronousQueue<Runnable>() );
private static ThreadPoolExecutor downloadPool = new ThreadPoolExecutor( 0, 5, 6, TimeUnit.MINUTES, new SynchronousQueue<Runnable>() );
private static Listener listener;
static {
log.debug( "Starting listener on port " + Globals.getFiletransferPortSsl() );
try {
String pathToKeyStore = Globals.getSslKeystoreFile();
char[] passphrase = Globals.getSslKeystorePassword().toCharArray();
KeyStore keystore = KeyStore.getInstance( "JKS" );
keystore.load( new FileInputStream( pathToKeyStore ), passphrase );
KeyManagerFactory kmf = KeyManagerFactory.getInstance( KeyManagerFactory.getDefaultAlgorithm() );
kmf.init( keystore, passphrase );
sslContext = SSLContext.getInstance( "TLSv1.2" );
KeyManager[] keyManagers = kmf.getKeyManagers();
sslContext.init( keyManagers, null, null );
listener = new Listener( eventHandler, sslContext, Globals.getFiletransferPortSsl(), Globals.getFiletransferTimeout() * 1000 );
listener.start();
} catch ( Exception e ) {
log.error( "Initialization failed.", e );
System.exit( 2 );
}
}
/**
* Checks if this image is already uploading and returns a new list with missing blocks if so.
* Puts the new image into processing list else.
*
* @param serverSessionId The uploading server
* @param imageData The data of the image
* @return
* @throws UploadException If some error occurred during the process
*/
public static TransferInformation getUploadInfos( ImagePublishData imageData, List<Integer> crcSums )
throws TTransferRejectedException, TInvocationException
{
// check image data
if ( Util.isEmpty( imageData.imageName ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Image name not set" );
if ( !DbUser.exists( imageData.user ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Invalid or missing image owner" );
if ( DbOsVirt.osExists( imageData.osId ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Content operating system not set" );
if ( DbOsVirt.virtExists( imageData.virtId ) )
throw new TInvocationException( InvocationError.INVALID_DATA, "Content virtualizer system not set" );
if ( imageData.fileSize <= 0 )
throw new TInvocationException( InvocationError.INVALID_DATA, "File size is too small" );
log.debug( "A satellite is submitting " + imageData.imageVersionId );
final String uuid = imageData.imageVersionId;
final String filepathRelative;
final CrcFile crcFile;
if ( crcSums == null ) {
crcFile = null;
} else {
crcFile = new CrcFile( crcSums );
}
ImagePublishData image;
synchronized ( pendingIncomingUploads ) {
/*
// check if image is already uploading
if ( ( image = uploadingImages.get( uuid ) ) == null ) {
// TODO insert new image to DB
uploadingImages.put( uuid, image );
}
*/
}
final String token = RandomString.generate( 50, false );
// TODO addUpload( token, image );
// TODO Set crc file on image - if there is already a crc file assigned, this does nothing
return new TransferInformation( token, Globals.getFiletransferPortPlain(), Globals.getFiletransferPortSsl() );
}
/**
* Add a new allowed incoming upload connection
* for the given token and image.
*
* @param token The unique token
* @param image Image being uploaded
*/
public static void addUpload( String token, AbstractTransfer image )
{
pendingIncomingUploads.put( token, image );
log.debug( "Added upload" );
}
/**
* Add a new allowed incoming download connection
* for the given token and image.
*
* @param token The unique token
* @param image Image being uploaded
*/
public static void addDownload( String token, AbstractTransfer image )
{
pendingIncomingDownloads.put( token, image );
log.debug( "Added download" );
}
/**
* Server is uploading - client is downloading!
*/
@Override
public void incomingDownloadRequest( final Uploader uploader )
{
// TODO
uploader.sendErrorCode( "Too many concurrent uploads." );
uploader.cancel();
}
/**
* Server is downloading - client is uploading!
*/
@Override
public void incomingUploadRequest( final Downloader downloader ) throws IOException
{
// TODO
downloader.sendErrorCode( "Too many concurrent downloads." );
downloader.cancel();
}
}