/*
* ftp4j - A pure Java FTP client library
*
* Copyright (C) 2008-2010 Carlo Pelliccia (www.sauronsoftware.it)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License version
* 2.1, as published by the Free Software Foundation.
*
* 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 Lesser General Public License 2.1 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License version 2.1 along with this program.
* If not, see .
*/
package it.sauronsoftware.ftp4j;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.ArrayList;
import java.util.StringTokenizer;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class implements a local server to make data transfer with the remote
* FTP server.
*
* @author Carlo Pelliccia
*/
class FTPDataTransferServer implements FTPDataTransferConnectionProvider,
Runnable {
private static final Logger logger= org.das2.util.LoggerManager.getLogger("das2.filesystem.ftp");
/**
* The ServerSocket object waiting for the incoming connection.
*/
private ServerSocket serverSocket = null;
/**
* The socket to be established with the remote host.
*/
private Socket socket;
/**
* The exception thrown during the wait for a connection (if any!).
*/
private IOException exception;
/**
* The thread executing the listening for incoming connection routine.
*/
private final Thread thread;
/**
* Build the object.
*
* @throws FTPDataTransferException
* If a I/O error occurs.
*/
public FTPDataTransferServer() throws FTPDataTransferException {
boolean useRange = false;
String aux = System.getProperty(FTPKeys.ACTIVE_DT_PORT_RANGE);
int start = 0;
int stop = 0;
if (aux != null) {
boolean valid = false;
StringTokenizer st = new StringTokenizer(aux, "-");
if (st.countTokens() == 2) {
String s1 = st.nextToken();
String s2 = st.nextToken();
int v1;
try {
v1 = Integer.parseInt(s1);
} catch (NumberFormatException e) {
v1 = 0;
}
int v2;
try {
v2 = Integer.parseInt(s2);
} catch (NumberFormatException e) {
v2 = 0;
}
if (v1 > 0 && v2 > 0 && v2 >= v1) {
start = v1;
stop = v2;
valid = true;
useRange = true;
}
}
if (!valid) {
// warning to the developer
logger.log(Level.WARNING,"WARNING: invalid value \"{0}\" for the "
+ FTPKeys.ACTIVE_DT_PORT_RANGE
+ " system property. The value should be in the start-stop form, with start > 0, stop > 0 and start <= stop.", aux);
}
}
if (useRange) {
ArrayList availables = new ArrayList<>();
for (int i = start; i <= stop; i++) {
availables.add(i);
}
int size;
boolean done = false;
while (!done && (size = availables.size()) > 0) {
int rand = (int) Math.floor(Math.random() * size);
int port = availables.remove(rand);
// Tries with the obtained value;
try {
serverSocket = new ServerSocket();
serverSocket.setReceiveBufferSize(512 * 1024);
serverSocket.bind(new InetSocketAddress(port));
done = true;
} catch (IOException e) {
// Port not available.
}
}
if (!done) {
throw new FTPDataTransferException(
"Cannot open the ServerSocket. "
+ "No available port found in range " + aux);
}
} else {
// Don't use a port range.
try {
serverSocket = new ServerSocket();
serverSocket.setReceiveBufferSize(512 * 1024);
serverSocket.bind(new InetSocketAddress(0));
} catch (IOException e) {
throw new FTPDataTransferException(
"Cannot open the ServerSocket", e);
}
}
// Starts the server thread.
thread = new Thread(this);
thread.start();
}
/**
* Returns the local port the server socket is bounded.
*
* @return The local port.
*/
public int getPort() {
return serverSocket.getLocalPort();
}
public void run() {
int timeout = 30000;
String aux = System.getProperty(FTPKeys.ACTIVE_DT_ACCEPT_TIMEOUT);
if (aux != null) {
boolean valid = false;
int value;
try {
value = Integer.parseInt(aux);
} catch (NumberFormatException e) {
value = -1;
}
if (value >= 0) {
timeout = value;
valid = true;
}
if (!valid) {
// warning to the developer
logger.warning("WARNING: invalid value \"" + aux
+ "\" for the " + FTPKeys.ACTIVE_DT_ACCEPT_TIMEOUT
+ " system property. The value should "
+ "be an integer greater or equal to 0.");
}
}
try {
// Set the socket timeout.
serverSocket.setSoTimeout(timeout);
// Wait for the incoming connection.
socket = serverSocket.accept();
socket.setSendBufferSize(512 * 1024);
} catch (IOException e) {
exception = e;
} finally {
// Close the server socket.
try {
serverSocket.close();
} catch (IOException e) {
;
}
}
}
/**
* Disposes the server and interrupts every operating stream.
*/
public void dispose() {
// Close the server socket (if open).
if (serverSocket != null) {
try {
serverSocket.close();
} catch (IOException e) {
;
}
}
}
public Socket openDataTransferConnection() throws FTPDataTransferException {
if (socket == null && exception == null) {
try {
thread.join();
} catch (Exception e) {
;
}
}
if (exception != null) {
throw new FTPDataTransferException(
"Cannot receive the incoming connection", exception);
}
if (socket == null) {
throw new FTPDataTransferException("No socket available");
}
return socket;
}
}