[Major] cleaned up the FileServer/Client implentation
Also renamed it from RMI to FileServer/Client. There is no reason this has to be RMI bound, as a client must simply implement FileClient where the upload/download methods can be implemented to be done over any kind of remote connection
This commit is contained in:
parent
5047ad9ff0
commit
9c547af5bb
|
@ -17,7 +17,7 @@
|
||||||
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package ch.eitchnet.rmi;
|
package ch.eitchnet.fileserver;
|
||||||
|
|
||||||
import java.rmi.RemoteException;
|
import java.rmi.RemoteException;
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ import java.rmi.RemoteException;
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public interface RMIFileClient {
|
public interface FileClient {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote method with which a client can push parts of files to the server. It is up to the client to send as many
|
* Remote method with which a client can push parts of files to the server. It is up to the client to send as many
|
||||||
|
@ -36,23 +36,23 @@ public interface RMIFileClient {
|
||||||
* @throws RemoteException
|
* @throws RemoteException
|
||||||
* if something goes wrong with the remote call
|
* if something goes wrong with the remote call
|
||||||
*/
|
*/
|
||||||
public void uploadFilePart(RmiFilePart filePart) throws RemoteException;
|
public void uploadFilePart(FilePart filePart) throws RemoteException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote method with which a client can delete files from the server. It only deletes single files if they exist
|
* Remote method with which a client can delete files from the server. It only deletes single files if they exist
|
||||||
*
|
*
|
||||||
* @param fileDeletion
|
* @param fileDeletion
|
||||||
* the {@link RmiFileDeletion} defining the deletion request
|
* the {@link FileDeletion} defining the deletion request
|
||||||
*
|
*
|
||||||
* @return true if the file was deleted, false if the file did not exist
|
* @return true if the file was deleted, false if the file did not exist
|
||||||
*
|
*
|
||||||
* @throws RemoteException
|
* @throws RemoteException
|
||||||
* if something goes wrong with the remote call
|
* if something goes wrong with the remote call
|
||||||
*/
|
*/
|
||||||
public boolean deleteFile(RmiFileDeletion fileDeletion) throws RemoteException;
|
public boolean deleteFile(FileDeletion fileDeletion) throws RemoteException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote method which a client can request part of a file. The server will fill the given {@link RmiFilePart} with
|
* Remote method which a client can request part of a file. The server will fill the given {@link FilePart} with
|
||||||
* a byte array of the file, with bytes from the file, respecting the desired offset. It is up to the client to call
|
* a byte array of the file, with bytes from the file, respecting the desired offset. It is up to the client to call
|
||||||
* this method multiple times for the entire file. It is a decision of the concrete implementation how much data is
|
* this method multiple times for the entire file. It is a decision of the concrete implementation how much data is
|
||||||
* returned in each part, the client may pass a request, but this is not definitive
|
* returned in each part, the client may pass a request, but this is not definitive
|
||||||
|
@ -65,5 +65,5 @@ public interface RMIFileClient {
|
||||||
* @throws RemoteException
|
* @throws RemoteException
|
||||||
* if something goes wrong with the remote call
|
* if something goes wrong with the remote call
|
||||||
*/
|
*/
|
||||||
public RmiFilePart requestFile(RmiFilePart filePart) throws RemoteException;
|
public FilePart requestFile(FilePart filePart) throws RemoteException;
|
||||||
}
|
}
|
|
@ -17,13 +17,14 @@
|
||||||
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package ch.eitchnet.rmi;
|
package ch.eitchnet.fileserver;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
import java.io.BufferedInputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.rmi.RemoteException;
|
import java.rmi.RemoteException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -35,25 +36,27 @@ import ch.eitchnet.utils.helper.StringHelper;
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public class RmiHelper {
|
public class FileClientUtil {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RmiHelper.class);
|
private static final Logger logger = LoggerFactory.getLogger(FileClientUtil.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param rmiFileClient
|
* @param rmiFileClient
|
||||||
* @param origFilePart
|
* @param origFilePart
|
||||||
* @param dstFile
|
* @param dstFile
|
||||||
*/
|
*/
|
||||||
public static void downloadFile(RMIFileClient rmiFileClient, RmiFilePart origFilePart, File dstFile) {
|
public static void downloadFile(FileClient rmiFileClient, FilePart origFilePart, File dstFile) {
|
||||||
|
|
||||||
// here we don't overwrite, the caller must make sure the destination file does not exist
|
// here we don't overwrite, the caller must make sure the destination file does not exist
|
||||||
if (dstFile.exists())
|
if (dstFile.exists()) {
|
||||||
throw new RuntimeException("The destination file " + dstFile.getAbsolutePath()
|
String msg = "The destination file {0} already exists. Delete it first, if you want to overwrite it!"; //$NON-NLS-1$
|
||||||
+ " already exists. Delete it first, if you want to overwrite it!");
|
msg = MessageFormat.format(msg, dstFile.getAbsolutePath());
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
RmiFilePart tmpPart = origFilePart;
|
FilePart tmpPart = origFilePart;
|
||||||
|
|
||||||
int loops = 0;
|
int loops = 0;
|
||||||
int startLength = tmpPart.getPartLength();
|
int startLength = tmpPart.getPartLength();
|
||||||
|
@ -64,14 +67,17 @@ public class RmiHelper {
|
||||||
tmpPart = rmiFileClient.requestFile(tmpPart);
|
tmpPart = rmiFileClient.requestFile(tmpPart);
|
||||||
|
|
||||||
// validate length of data
|
// validate length of data
|
||||||
if (tmpPart.getPartLength() != tmpPart.getPartBytes().length)
|
if (tmpPart.getPartLength() != tmpPart.getPartBytes().length) {
|
||||||
throw new RuntimeException("Invalid tmpPart. Part length is not as long as the bytes passed "
|
String msg = "Invalid tmpPart. Part length is not as long as the bytes passed {0} / {1}"; //$NON-NLS-1$
|
||||||
+ tmpPart.getPartLength() + " / " + tmpPart.getPartBytes().length);
|
msg = MessageFormat.format(msg, tmpPart.getPartLength(), tmpPart.getPartBytes().length);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// validate offset is size of file
|
// validate offset is size of file
|
||||||
if (tmpPart.getPartOffset() != dstFile.length()) {
|
if (tmpPart.getPartOffset() != dstFile.length()) {
|
||||||
throw new RuntimeException("The part offset $offset is not at the end of the file "
|
String msg = "The part offset $offset is not at the end of the file {0} / {1}"; //$NON-NLS-1$
|
||||||
+ tmpPart.getPartOffset() + " / " + dstFile.length());
|
msg = MessageFormat.format(msg, tmpPart.getPartOffset(), dstFile.length());
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// append the part
|
// append the part
|
||||||
|
@ -84,28 +90,33 @@ public class RmiHelper {
|
||||||
if (tmpPart.getPartOffset() >= tmpPart.getFileLength())
|
if (tmpPart.getPartOffset() >= tmpPart.getFileLength())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
RmiHelper.logger.info(tmpPart.getFileType() + ": " + tmpPart.getFileName() + ": Requested " + loops
|
|
||||||
+ " parts. StartSize: " + startLength + " EndSize: " + tmpPart.getPartLength());
|
String msg = "{0}: {1}: Requested {2} parts. StartSize: {3} EndSize: {4}"; //$NON-NLS-1$
|
||||||
|
msg = MessageFormat.format(msg, tmpPart.getFileType(), tmpPart.getFileName(), loops, startLength,
|
||||||
|
tmpPart.getPartLength());
|
||||||
|
logger.info(msg);
|
||||||
|
|
||||||
// validate that the offset is at the end of the file
|
// validate that the offset is at the end of the file
|
||||||
if (tmpPart.getPartOffset() != origFilePart.getFileLength()) {
|
if (tmpPart.getPartOffset() != origFilePart.getFileLength()) {
|
||||||
throw new RuntimeException("Offset " + tmpPart.getPartOffset() + " is not at file length "
|
msg = "Offset {0} is not at file length {1} after reading all the file parts!"; //$NON-NLS-1$
|
||||||
+ origFilePart.getFileLength() + " after reading all the file parts!");
|
msg = MessageFormat.format(msg, tmpPart.getPartOffset(), origFilePart.getFileLength());
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// now validate hashes
|
// now validate hashes
|
||||||
String dstFileHash = StringHelper.getHexString(FileHelper.hashFileSha256(dstFile));
|
String dstFileHash = StringHelper.getHexString(FileHelper.hashFileSha256(dstFile));
|
||||||
if (!dstFileHash.equals(origFilePart.getFileHash())) {
|
if (!dstFileHash.equals(origFilePart.getFileHash())) {
|
||||||
throw new RuntimeException("Downloading the file " + origFilePart.getFileName()
|
msg = "Downloading the file {0} failed because the hashes don''t match. Expected: {1} / Actual: {2}"; //$NON-NLS-1$
|
||||||
+ " failed because the hashes don't match. Expected: " + origFilePart.getFileHash()
|
msg = MessageFormat.format(msg, origFilePart.getFileName(), origFilePart.getFileHash(), dstFileHash);
|
||||||
+ " / Actual: " + dstFileHash);
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof RuntimeException)
|
if (e instanceof RuntimeException)
|
||||||
throw (RuntimeException) e;
|
throw (RuntimeException) e;
|
||||||
throw new RuntimeException("Downloading the file " + origFilePart.getFileName()
|
String msg = "Downloading the file {0} failed because of an underlying exception {1}"; //$NON-NLS-1$
|
||||||
+ " failed because of an underlying exception " + e.getLocalizedMessage());
|
msg = MessageFormat.format(msg, origFilePart.getFileName(), e.getLocalizedMessage());
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,28 +125,29 @@ public class RmiHelper {
|
||||||
* @param srcFile
|
* @param srcFile
|
||||||
* @param fileType
|
* @param fileType
|
||||||
*/
|
*/
|
||||||
public static void uploadFile(RMIFileClient rmiFileClient, File srcFile, String fileType) {
|
public static void uploadFile(FileClient rmiFileClient, File srcFile, String fileType) {
|
||||||
|
|
||||||
// make sure the source file exists
|
// make sure the source file exists
|
||||||
if (!srcFile.canRead())
|
if (!srcFile.canRead()) {
|
||||||
throw new RuntimeException("The source file does not exist at " + srcFile.getAbsolutePath());
|
String msg = MessageFormat.format("The source file does not exist at {0}", srcFile.getAbsolutePath()); //$NON-NLS-1$
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
BufferedInputStream inputStream = null;
|
try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(srcFile));) {
|
||||||
try {
|
|
||||||
|
|
||||||
// get the size of the file
|
// get the size of the file
|
||||||
long fileLength = srcFile.length();
|
long fileLength = srcFile.length();
|
||||||
String fileHash = StringHelper.getHexString(FileHelper.hashFileSha256(srcFile));
|
String fileHash = StringHelper.getHexString(FileHelper.hashFileSha256(srcFile));
|
||||||
|
|
||||||
// create the file part to send
|
// create the file part to send
|
||||||
RmiFilePart filePart = new RmiFilePart(srcFile.getName(), fileType);
|
FilePart filePart = new FilePart(srcFile.getName(), fileType);
|
||||||
filePart.setFileLength(fileLength);
|
filePart.setFileLength(fileLength);
|
||||||
filePart.setFileHash(fileHash);
|
filePart.setFileHash(fileHash);
|
||||||
|
|
||||||
// define the normal size of the parts we're sending. The last part will naturally have a different size
|
// define the normal size of the parts we're sending. The last part will naturally have a different size
|
||||||
int partLength;
|
int partLength;
|
||||||
if (fileLength > RmiFileHandler.MAX_PART_SIZE)
|
if (fileLength > FileHandler.MAX_PART_SIZE)
|
||||||
partLength = RmiFileHandler.MAX_PART_SIZE;
|
partLength = FileHandler.MAX_PART_SIZE;
|
||||||
else
|
else
|
||||||
partLength = (int) fileLength;
|
partLength = (int) fileLength;
|
||||||
|
|
||||||
|
@ -143,8 +155,6 @@ public class RmiHelper {
|
||||||
byte[] bytes = new byte[partLength];
|
byte[] bytes = new byte[partLength];
|
||||||
|
|
||||||
// open the stream to the file
|
// open the stream to the file
|
||||||
inputStream = new BufferedInputStream(new FileInputStream(srcFile));
|
|
||||||
|
|
||||||
int read = 0;
|
int read = 0;
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
|
|
||||||
|
@ -159,11 +169,11 @@ public class RmiHelper {
|
||||||
|
|
||||||
// validate we read the expected number of bytes
|
// validate we read the expected number of bytes
|
||||||
if (read == -1)
|
if (read == -1)
|
||||||
throw new IOException("Something went wrong while reading the bytes as -1 was returned!");
|
throw new IOException("Something went wrong while reading the bytes as -1 was returned!"); //$NON-NLS-1$
|
||||||
if (read != bytes.length) {
|
if (read != bytes.length) {
|
||||||
throw new IOException(
|
String msg = "Something went wrong while reading the bytes as the wrong number of bytes were read. Expected {0} Actual: {1}"; //$NON-NLS-1$
|
||||||
"Something went wrong while reading the bytes as the wrong number of bytes were read. Expected "
|
msg = MessageFormat.format(msg, bytes.length, read);
|
||||||
+ bytes.length + " Actual: " + read);
|
throw new IOException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// set the fields on the FilePart
|
// set the fields on the FilePart
|
||||||
|
@ -187,9 +197,11 @@ public class RmiHelper {
|
||||||
// the last part of the file
|
// the last part of the file
|
||||||
if (nextOffset + bytes.length > fileLength) {
|
if (nextOffset + bytes.length > fileLength) {
|
||||||
long remaining = fileLength - nextOffset;
|
long remaining = fileLength - nextOffset;
|
||||||
if (remaining > RmiFileHandler.MAX_PART_SIZE)
|
if (remaining > FileHandler.MAX_PART_SIZE) {
|
||||||
throw new RuntimeException("Something went wrong as the remaining part " + remaining
|
String msg = "Something went wrong as the remaining part {0} is larger than MAX_PART_SIZE {1}!"; //$NON-NLS-1$
|
||||||
+ " is larger than MAX_PART_SIZE " + RmiFileHandler.MAX_PART_SIZE + "!");
|
msg = MessageFormat.format(msg, remaining, FileHandler.MAX_PART_SIZE);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
partLength = (int) remaining;
|
partLength = (int) remaining;
|
||||||
bytes = new byte[partLength];
|
bytes = new byte[partLength];
|
||||||
}
|
}
|
||||||
|
@ -198,22 +210,17 @@ public class RmiHelper {
|
||||||
offset = nextOffset;
|
offset = nextOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
RmiHelper.logger.info(filePart.getFileType() + ": " + filePart.getFileName() + ": Sent " + loops
|
String msg = "{0}: {1}: Sent {2} parts. StartSize: {3} EndSize: {4}"; //$NON-NLS-1$
|
||||||
+ " parts. StartSize: " + startLength + " EndSize: " + filePart.getPartLength());
|
msg = MessageFormat.format(msg, filePart.getFileType(), filePart.getFileName(), loops, startLength,
|
||||||
|
filePart.getPartLength());
|
||||||
|
logger.info(msg);
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
if (e instanceof RuntimeException)
|
if (e instanceof RuntimeException)
|
||||||
throw (RuntimeException) e;
|
throw (RuntimeException) e;
|
||||||
throw new RuntimeException("Uploading the file " + srcFile.getAbsolutePath()
|
String msg = "Uploading the file {0} failed because of an underlying exception {1}"; //$NON-NLS-1$
|
||||||
+ " failed because of an underlying exception " + e.getLocalizedMessage());
|
msg = MessageFormat.format(msg, srcFile.getAbsolutePath(), e.getLocalizedMessage());
|
||||||
} finally {
|
throw new RuntimeException(msg);
|
||||||
if (inputStream != null) {
|
|
||||||
try {
|
|
||||||
inputStream.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
RmiHelper.logger.error("Exception while closing FileInputStream " + e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -222,13 +229,14 @@ public class RmiHelper {
|
||||||
* @param fileDeletion
|
* @param fileDeletion
|
||||||
* @param dstFile
|
* @param dstFile
|
||||||
*/
|
*/
|
||||||
public static void deleteFile(RMIFileClient rmiFileClient, RmiFileDeletion fileDeletion, File dstFile) {
|
public static void deleteFile(FileClient rmiFileClient, FileDeletion fileDeletion, File dstFile) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
rmiFileClient.deleteFile(fileDeletion);
|
rmiFileClient.deleteFile(fileDeletion);
|
||||||
} catch (RemoteException e) {
|
} catch (RemoteException e) {
|
||||||
throw new RuntimeException("Deleting the file " + fileDeletion.getFileName()
|
String msg = "Deleting the file {0} failed because of an underlying exception {1}"; //$NON-NLS-1$
|
||||||
+ " failed because of an underlying exception " + e.getLocalizedMessage());
|
msg = MessageFormat.format(msg, fileDeletion.getFileName(), e.getLocalizedMessage());
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,16 +17,15 @@
|
||||||
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package ch.eitchnet.rmi;
|
package ch.eitchnet.fileserver;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*/
|
*/
|
||||||
public class RmiFileDeletion implements Serializable {
|
public class FileDeletion implements Serializable {
|
||||||
|
|
||||||
//
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
@ -38,7 +37,7 @@ public class RmiFileDeletion implements Serializable {
|
||||||
* @param fileType
|
* @param fileType
|
||||||
* the type of file to delete. This defines in which path the file resides
|
* the type of file to delete. This defines in which path the file resides
|
||||||
*/
|
*/
|
||||||
public RmiFileDeletion(String fileName, String fileType) {
|
public FileDeletion(String fileName, String fileType) {
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.fileType = fileType;
|
this.fileType = fileType;
|
||||||
}
|
}
|
|
@ -17,12 +17,13 @@
|
||||||
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package ch.eitchnet.rmi;
|
package ch.eitchnet.fileserver;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -32,13 +33,13 @@ import ch.eitchnet.utils.helper.StringHelper;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class handles remote requests of clients to upload or download a file. Uploading a file is done by calling
|
* This class handles remote requests of clients to upload or download a file. Uploading a file is done by calling
|
||||||
* {@link #handleFilePart(RmiFilePart)} and the downloading a file is done by calling {@link #requestFile(RmiFilePart)}
|
* {@link #handleFilePart(FilePart)} and the downloading a file is done by calling {@link #requestFile(FilePart)}
|
||||||
*
|
*
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*/
|
*/
|
||||||
public class RmiFileHandler {
|
public class FileHandler {
|
||||||
|
|
||||||
private static final Logger logger = LoggerFactory.getLogger(RmiFileHandler.class);
|
private static final Logger logger = LoggerFactory.getLogger(FileHandler.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* DEF_PART_SIZE = default part size which is set to 1048576 bytes (1 MiB)
|
* DEF_PART_SIZE = default part size which is set to 1048576 bytes (1 MiB)
|
||||||
|
@ -46,23 +47,29 @@ public class RmiFileHandler {
|
||||||
public static final int MAX_PART_SIZE = 1048576;
|
public static final int MAX_PART_SIZE = 1048576;
|
||||||
|
|
||||||
private String basePath;
|
private String basePath;
|
||||||
|
private boolean verbose;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public RmiFileHandler(String basePath) {
|
public FileHandler(String basePath, boolean verbose) {
|
||||||
|
|
||||||
File basePathF = new File(basePath);
|
File basePathF = new File(basePath);
|
||||||
if (!basePathF.exists())
|
if (!basePathF.exists()) {
|
||||||
throw new RuntimeException("Base Path does not exist " + basePathF.getAbsolutePath());
|
String msg = MessageFormat.format("Base Path does not exist {0}", basePathF.getAbsolutePath()); //$NON-NLS-1$
|
||||||
if (!basePathF.canWrite())
|
throw new RuntimeException(msg);
|
||||||
throw new RuntimeException("Can not write to base path " + basePathF.getAbsolutePath());
|
}
|
||||||
|
if (!basePathF.canWrite()) {
|
||||||
|
String msg = MessageFormat.format("Can not write to base path {0}", basePathF.getAbsolutePath()); //$NON-NLS-1$
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.verbose = verbose;
|
||||||
this.basePath = basePath;
|
this.basePath = basePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method which a client can request part of a file. The server will fill the given {@link RmiFilePart} with a byte
|
* Method which a client can request part of a file. The server will fill the given {@link FilePart} with a byte
|
||||||
* array of the file, with bytes from the file, respecting the desired offset. It is up to the client to call this
|
* array of the file, with bytes from the file, respecting the desired offset. It is up to the client to call this
|
||||||
* method multiple times for the entire file. It is a decision of the concrete implementation how much data is
|
* method multiple times for the entire file. It is a decision of the concrete implementation how much data is
|
||||||
* returned in each part, the client may pass a request, but this is not definitive
|
* returned in each part, the client may pass a request, but this is not definitive
|
||||||
|
@ -70,7 +77,7 @@ public class RmiFileHandler {
|
||||||
* @param filePart
|
* @param filePart
|
||||||
* the part of the file
|
* the part of the file
|
||||||
*/
|
*/
|
||||||
public RmiFilePart requestFile(RmiFilePart filePart) {
|
public FilePart requestFile(FilePart filePart) {
|
||||||
|
|
||||||
// validate file name is legal
|
// validate file name is legal
|
||||||
String fileName = filePart.getFileName();
|
String fileName = filePart.getFileName();
|
||||||
|
@ -81,12 +88,15 @@ public class RmiFileHandler {
|
||||||
validateFileType(fileType);
|
validateFileType(fileType);
|
||||||
|
|
||||||
// evaluate the path where the file should reside
|
// evaluate the path where the file should reside
|
||||||
File file = new File(this.basePath + "/" + fileType, filePart.getFileName());
|
String fileTypePath = this.basePath + "/" + fileType; //$NON-NLS-1$
|
||||||
|
File file = new File(fileTypePath, filePart.getFileName());
|
||||||
|
|
||||||
// now evaluate the file exists
|
// now evaluate the file exists
|
||||||
|
String fileNotFoundMsg = "The file {0} could not be found in the location for files of type {1}"; //$NON-NLS-1$
|
||||||
if (!file.canRead()) {
|
if (!file.canRead()) {
|
||||||
throw new RuntimeException("The file " + fileName
|
String msg = fileNotFoundMsg;
|
||||||
+ " could not be found in the location for files of type " + fileType);
|
msg = MessageFormat.format(msg, fileName, fileType);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// if this is the start of the file, then prepare the file part
|
// if this is the start of the file, then prepare the file part
|
||||||
|
@ -103,18 +113,21 @@ public class RmiFileHandler {
|
||||||
// variables defining the part of the file we're going to return
|
// variables defining the part of the file we're going to return
|
||||||
long requestOffset = filePart.getPartOffset();
|
long requestOffset = filePart.getPartOffset();
|
||||||
int requestSize = filePart.getPartLength();
|
int requestSize = filePart.getPartLength();
|
||||||
if (requestSize > RmiFileHandler.MAX_PART_SIZE) {
|
if (requestSize > FileHandler.MAX_PART_SIZE) {
|
||||||
throw new RuntimeException("The requested part size " + requestSize + " is greater than the allowed "
|
String msg = "The requested part size {0} is greater than the allowed {1}"; //$NON-NLS-1$
|
||||||
+ RmiFileHandler.MAX_PART_SIZE);
|
msg = MessageFormat.format(msg, requestSize, MAX_PART_SIZE);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// validate lengths and offsets
|
// validate lengths and offsets
|
||||||
if (filePart.getFileLength() != fileSize) {
|
if (filePart.getFileLength() != fileSize) {
|
||||||
throw new RuntimeException("The part request has a file size " + filePart.getFileLength()
|
String msg = "The part request has a file size {0}, but the file is actually {1}"; //$NON-NLS-1$
|
||||||
+ ", but the file is actually " + fileSize);
|
msg = MessageFormat.format(msg, filePart.getFileLength(), fileSize);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
} else if (requestOffset > fileSize) {
|
} else if (requestOffset > fileSize) {
|
||||||
throw new RuntimeException("The requested file part offset " + requestOffset
|
String msg = "The requested file part offset {0} is greater than the size of the file {1}"; //$NON-NLS-1$
|
||||||
+ " is greater than the size of the file " + fileSize);
|
msg = MessageFormat.format(msg, requestOffset, fileSize);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
// Otherwise make sure the offset + request length is not larger than the actual file size.
|
// Otherwise make sure the offset + request length is not larger than the actual file size.
|
||||||
// If it is then this is the end part
|
// If it is then this is the end part
|
||||||
|
@ -126,8 +139,11 @@ public class RmiFileHandler {
|
||||||
long l = Math.min(requestSize, remaining);
|
long l = Math.min(requestSize, remaining);
|
||||||
|
|
||||||
// this is a fail safe
|
// this is a fail safe
|
||||||
if (l > RmiFileHandler.MAX_PART_SIZE)
|
if (l > MAX_PART_SIZE) {
|
||||||
throw new RuntimeException("Something went wrong. Min of requestSize and remaining is > MAX_PART_SIZE!");
|
String msg = "Something went wrong. Min of requestSize and remaining is > MAX_PART_SIZE of {0}!"; //$NON-NLS-1$
|
||||||
|
msg = MessageFormat.format(msg, MAX_PART_SIZE);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// this is the size of the array we want to return
|
// this is the size of the array we want to return
|
||||||
requestSize = (int) l;
|
requestSize = (int) l;
|
||||||
|
@ -136,40 +152,42 @@ public class RmiFileHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// now read the part of the file and set it as bytes for the file part
|
// now read the part of the file and set it as bytes for the file part
|
||||||
FileInputStream fin = null;
|
try (FileInputStream fin = new FileInputStream(file);) {
|
||||||
try {
|
|
||||||
|
|
||||||
// position the stream
|
// position the stream
|
||||||
fin = new FileInputStream(file);
|
|
||||||
long skip = fin.skip(requestOffset);
|
long skip = fin.skip(requestOffset);
|
||||||
if (skip != requestOffset)
|
if (skip != requestOffset) {
|
||||||
throw new IOException("Asked to skip " + requestOffset + " but only skipped " + skip);
|
String msg = MessageFormat.format("Asked to skip {0} but only skipped {1}", requestOffset, skip); //$NON-NLS-1$
|
||||||
|
throw new IOException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// read the data
|
// read the data
|
||||||
byte[] bytes = new byte[requestSize];
|
byte[] bytes = new byte[requestSize];
|
||||||
int read = fin.read(bytes);
|
int read = fin.read(bytes);
|
||||||
if (read != requestSize)
|
if (read != requestSize) {
|
||||||
throw new IOException("Asked to read " + requestSize + " but only read " + read);
|
String msg = MessageFormat.format("Asked to read {0} but only read {1}", requestSize, read); //$NON-NLS-1$
|
||||||
|
throw new IOException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
// set the return result
|
// set the return result
|
||||||
filePart.setPartBytes(bytes);
|
filePart.setPartBytes(bytes);
|
||||||
|
|
||||||
} catch (FileNotFoundException e) {
|
} catch (FileNotFoundException e) {
|
||||||
throw new RuntimeException("The file " + fileName
|
String msg = MessageFormat.format(fileNotFoundMsg, fileName, fileType);
|
||||||
+ " could not be found in the location for files of type " + fileType);
|
throw new RuntimeException(msg);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new RuntimeException("There was an error while reading from the file " + fileName);
|
String msg = "There was an error while reading from the file {0}"; //$NON-NLS-1$
|
||||||
} finally {
|
msg = MessageFormat.format(msg, fileName);
|
||||||
if (fin != null) {
|
throw new RuntimeException(msg);
|
||||||
try {
|
|
||||||
fin.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
RmiFileHandler.logger.error("Error while closing FileInputStream: " + e.getLocalizedMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are returning the same object as the user gave us, just edited
|
// we are returning the same object as the user gave us, just modified
|
||||||
|
if (this.verbose) {
|
||||||
|
String msg = "Read {0} for file {1}/{2}"; //$NON-NLS-1$
|
||||||
|
String fileSizeS = FileHelper.humanizeFileSize(filePart.getPartBytes().length);
|
||||||
|
msg = MessageFormat.format(msg, fileSizeS, fileType, fileName);
|
||||||
|
logger.info(msg);
|
||||||
|
}
|
||||||
return filePart;
|
return filePart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,7 +198,7 @@ public class RmiFileHandler {
|
||||||
* @param filePart
|
* @param filePart
|
||||||
* the part of the file
|
* the part of the file
|
||||||
*/
|
*/
|
||||||
public void handleFilePart(RmiFilePart filePart) {
|
public void handleFilePart(FilePart filePart) {
|
||||||
|
|
||||||
// validate file name is legal
|
// validate file name is legal
|
||||||
String fileName = filePart.getFileName();
|
String fileName = filePart.getFileName();
|
||||||
|
@ -191,11 +209,14 @@ public class RmiFileHandler {
|
||||||
validateFileType(fileType);
|
validateFileType(fileType);
|
||||||
|
|
||||||
// evaluate the path where the file should reside
|
// evaluate the path where the file should reside
|
||||||
File dstFile = new File(this.basePath + "/" + fileType, filePart.getFileName());
|
String fileTypePath = this.basePath + "/" + fileType; //$NON-NLS-1$
|
||||||
|
File dstFile = new File(fileTypePath, filePart.getFileName());
|
||||||
|
|
||||||
// if the file already exists, then this may not be a start part
|
// if the file already exists, then this may not be a start part
|
||||||
if (filePart.getPartOffset() == 0 && dstFile.exists()) {
|
if (filePart.getPartOffset() == 0 && dstFile.exists()) {
|
||||||
throw new RuntimeException("The file " + fileName + " already exist for type " + fileType);
|
String msg = "The file {0} already exist for type {1}"; //$NON-NLS-1$
|
||||||
|
msg = MessageFormat.format(msg, fileName, fileType);
|
||||||
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// write the part
|
// write the part
|
||||||
|
@ -205,23 +226,34 @@ public class RmiFileHandler {
|
||||||
if (filePart.isLastPart()) {
|
if (filePart.isLastPart()) {
|
||||||
String dstFileHash = StringHelper.getHexString(FileHelper.hashFileSha256(dstFile));
|
String dstFileHash = StringHelper.getHexString(FileHelper.hashFileSha256(dstFile));
|
||||||
if (!dstFileHash.equals(filePart.getFileHash())) {
|
if (!dstFileHash.equals(filePart.getFileHash())) {
|
||||||
throw new RuntimeException("Uploading the file " + filePart.getFileName()
|
String msg = "Uploading the file {0} failed because the hashes don''t match. Expected: {1} / Actual: {2}"; //$NON-NLS-1$
|
||||||
+ " failed because the hashes don't match. Expected: " + filePart.getFileHash() + " / Actual: "
|
msg = MessageFormat.format(msg, filePart.getFileName(), filePart.getFileHash(), dstFileHash);
|
||||||
+ dstFileHash);
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this.verbose) {
|
||||||
|
String msg;
|
||||||
|
if (filePart.isLastPart())
|
||||||
|
msg = "Wrote {0} for part of file {1}/{2}"; //$NON-NLS-1$
|
||||||
|
else
|
||||||
|
msg = "Wrote {0} for last part of file {1}/{2}"; //$NON-NLS-1$
|
||||||
|
String fileSizeS = FileHelper.humanizeFileSize(filePart.getPartBytes().length);
|
||||||
|
msg = MessageFormat.format(msg, fileSizeS, fileType, fileName);
|
||||||
|
logger.info(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Method with which a client can delete files from the server. It only deletes single files if they exist
|
* Method with which a client can delete files from the server. It only deletes single files if they exist
|
||||||
*
|
*
|
||||||
* @param fileDeletion
|
* @param fileDeletion
|
||||||
* the {@link RmiFileDeletion} defining the deletion request
|
* the {@link FileDeletion} defining the deletion request
|
||||||
*
|
*
|
||||||
* @return true if the file was deleted, false if the file did not exist
|
* @return true if the file was deleted, false if the file did not exist
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public boolean deleteFile(RmiFileDeletion fileDeletion) {
|
public boolean deleteFile(FileDeletion fileDeletion) {
|
||||||
|
|
||||||
// validate file name is legal
|
// validate file name is legal
|
||||||
String fileName = fileDeletion.getFileName();
|
String fileName = fileDeletion.getFileName();
|
||||||
|
@ -232,10 +264,20 @@ public class RmiFileHandler {
|
||||||
validateFileType(fileType);
|
validateFileType(fileType);
|
||||||
|
|
||||||
// evaluate the path where the file should reside
|
// evaluate the path where the file should reside
|
||||||
File fileToDelete = new File(this.basePath + "/" + fileType, fileDeletion.getFileName());
|
String fileTypePath = this.basePath + "/" + fileType; //$NON-NLS-1$
|
||||||
|
File fileToDelete = new File(fileTypePath, fileDeletion.getFileName());
|
||||||
|
|
||||||
// delete the file
|
// delete the file
|
||||||
return FileHelper.deleteFiles(new File[] { fileToDelete }, true);
|
boolean deletedFile = FileHelper.deleteFiles(new File[] { fileToDelete }, true);
|
||||||
|
|
||||||
|
String msg;
|
||||||
|
if (deletedFile)
|
||||||
|
msg = "Deleted file {1}/{2}"; //$NON-NLS-1$
|
||||||
|
else
|
||||||
|
msg = "Failed to delete file {1}/{2}"; //$NON-NLS-1$
|
||||||
|
msg = MessageFormat.format(msg, fileType, fileName);
|
||||||
|
logger.info(msg);
|
||||||
|
return deletedFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -246,10 +288,10 @@ public class RmiFileHandler {
|
||||||
private void validateFileName(String fileName) {
|
private void validateFileName(String fileName) {
|
||||||
|
|
||||||
if (fileName == null || fileName.isEmpty()) {
|
if (fileName == null || fileName.isEmpty()) {
|
||||||
throw new RuntimeException("The file name was not given! Can not find a file without a name!");
|
throw new RuntimeException("The file name was not given! Can not find a file without a name!"); //$NON-NLS-1$
|
||||||
} else if (fileName.contains("/")) {
|
} else if (fileName.contains("/")) { //$NON-NLS-1$
|
||||||
throw new RuntimeException(
|
String msg = "The given file name contains illegal characters. The file name may not contain slashes!"; //$NON-NLS-1$
|
||||||
"The given file name contains illegal characters. The file name may not contain slashes!");
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,10 +302,10 @@ public class RmiFileHandler {
|
||||||
*/
|
*/
|
||||||
private void validateFileType(String fileType) {
|
private void validateFileType(String fileType) {
|
||||||
if (fileType == null || fileType.isEmpty()) {
|
if (fileType == null || fileType.isEmpty()) {
|
||||||
throw new RuntimeException("The file type was not given! Can not find a file without a type!");
|
throw new RuntimeException("The file type was not given! Can not find a file without a type!"); //$NON-NLS-1$
|
||||||
} else if (fileType.contains("/")) {
|
} else if (fileType.contains("/")) { //$NON-NLS-1$
|
||||||
throw new RuntimeException(
|
String msg = "The given file type contains illegal characters. The file type may not contain slashes!"; //$NON-NLS-1$
|
||||||
"The given file type contains illegal characters. The file type may not contain slashes!");
|
throw new RuntimeException(msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -17,16 +17,15 @@
|
||||||
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
* along with ch.eitchnet.java.utils. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
package ch.eitchnet.rmi;
|
package ch.eitchnet.fileserver;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Robert von Burg <eitch@eitchnet.ch>
|
* @author Robert von Burg <eitch@eitchnet.ch>
|
||||||
*/
|
*/
|
||||||
public class RmiFilePart implements Serializable {
|
public class FilePart implements Serializable {
|
||||||
|
|
||||||
//
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private String fileName;
|
private String fileName;
|
||||||
|
@ -45,18 +44,18 @@ public class RmiFilePart implements Serializable {
|
||||||
* @param fileType
|
* @param fileType
|
||||||
* defines the type of file being uploaded or retrieved. This defines in which path the file resides
|
* defines the type of file being uploaded or retrieved. This defines in which path the file resides
|
||||||
*/
|
*/
|
||||||
public RmiFilePart(String fileName, String fileType) {
|
public FilePart(String fileName, String fileType) {
|
||||||
|
|
||||||
if (fileName == null || fileName.isEmpty())
|
if (fileName == null || fileName.isEmpty())
|
||||||
throw new RuntimeException("fileName may not be empty!");
|
throw new RuntimeException("fileName may not be empty!"); //$NON-NLS-1$
|
||||||
if (fileType == null || fileType.isEmpty())
|
if (fileType == null || fileType.isEmpty())
|
||||||
throw new RuntimeException("fileType may not be empty!");
|
throw new RuntimeException("fileType may not be empty!"); //$NON-NLS-1$
|
||||||
|
|
||||||
this.fileName = fileName;
|
this.fileName = fileName;
|
||||||
this.fileType = fileType;
|
this.fileType = fileType;
|
||||||
|
|
||||||
this.partOffset = 0;
|
this.partOffset = 0;
|
||||||
this.partLength = RmiFileHandler.MAX_PART_SIZE;
|
this.partLength = FileHandler.MAX_PART_SIZE;
|
||||||
this.partBytes = null;
|
this.partBytes = null;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue