Many bugfixes and improvements
This commit is contained in:
parent
c3e3bbf787
commit
363e0c6cfc
52 changed files with 2021 additions and 982 deletions
57
src/zutil/net/update/FileInfo.java
Normal file
57
src/zutil/net/update/FileInfo.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
|
||||
import zutil.Hasher;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* This class is used to store the files
|
||||
* and there hashes
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class FileInfo implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private transient File file;
|
||||
private String path;
|
||||
private String hash;
|
||||
private long size;
|
||||
|
||||
public FileInfo(String root, File file) throws IOException{
|
||||
path = FileUtil.relativePath(file, root);
|
||||
hash = Hasher.MD5(file);
|
||||
size = file.length();
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
public String getHash() {
|
||||
return hash;
|
||||
}
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
public File getFile(){
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object comp){
|
||||
if(comp instanceof FileInfo){
|
||||
FileInfo tmp = (FileInfo)comp;
|
||||
return path.equals(tmp.path) && hash.equals(tmp.hash);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return path;
|
||||
}
|
||||
}
|
||||
82
src/zutil/net/update/FileListMessage.java
Normal file
82
src/zutil/net/update/FileListMessage.java
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* This class is used to store the files
|
||||
* and there hashes
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class FileListMessage implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private ArrayList<FileInfo> fileList;
|
||||
private long totalSize;
|
||||
|
||||
private FileListMessage(){}
|
||||
|
||||
/**
|
||||
* Returns a ArrayList of FileInfo object for all the files in the specified folder
|
||||
*
|
||||
* @param path is the path to scan
|
||||
**/
|
||||
public FileListMessage(String path) throws IOException{
|
||||
fileList = new ArrayList<FileInfo>();
|
||||
|
||||
List<File> files = FileUtil.search(FileUtil.find(path));
|
||||
long totalSize = 0;
|
||||
for(File file : files){
|
||||
FileInfo fileInfo = new FileInfo(path, file);
|
||||
fileList.add( fileInfo );
|
||||
totalSize += fileInfo.getSize();
|
||||
}
|
||||
this.totalSize = totalSize;
|
||||
}
|
||||
|
||||
|
||||
public long getTotalSize() {
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
public ArrayList<FileInfo> getFileList(){
|
||||
return fileList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the files and returns the files that differ from this file list
|
||||
*
|
||||
* @param comp is the file list to compare with
|
||||
* @return
|
||||
*/
|
||||
public FileListMessage getDiff( FileListMessage comp){
|
||||
FileListMessage diff = new FileListMessage();
|
||||
|
||||
long diffSize = 0;
|
||||
diff.fileList = new ArrayList<FileInfo>();
|
||||
for(FileInfo file : this.fileList){
|
||||
if( !comp.fileList.contains( file)){
|
||||
diff.fileList.add( file );
|
||||
diffSize += file.getSize();
|
||||
}
|
||||
}
|
||||
diff.totalSize = diffSize;
|
||||
|
||||
return diff;
|
||||
}
|
||||
|
||||
|
||||
public boolean equals(Object comp){
|
||||
if(comp instanceof FileListMessage){
|
||||
FileListMessage tmp = (FileListMessage)comp;
|
||||
return fileList.equals(tmp.fileList) && totalSize == tmp.totalSize;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
154
src/zutil/net/update/UpdateClient.java
Normal file
154
src/zutil/net/update/UpdateClient.java
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
/**
|
||||
* This class connects to a update server and updates a path
|
||||
* with the servers
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class UpdateClient{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private String path;
|
||||
private Socket socket;
|
||||
private FileListMessage fileList;
|
||||
|
||||
private long speed;
|
||||
private long totalReceived;
|
||||
private long expectedSize;
|
||||
private ProgressListener<UpdateClient,FileInfo> progress;
|
||||
|
||||
/**
|
||||
* Creates a UpdateClient
|
||||
*
|
||||
* @param address Address to the UpdateServer
|
||||
* @param port The port on the server
|
||||
* @param path Path to the files to update
|
||||
* @throws Exception
|
||||
*/
|
||||
public UpdateClient(String address, int port, String path) throws Exception{
|
||||
fileList = new FileListMessage(path);
|
||||
socket = new Socket(address, port);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener<UpdateClient,FileInfo> p){
|
||||
progress = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the files
|
||||
*/
|
||||
public void update() throws IOException{
|
||||
try{
|
||||
ObjectOutputStream out = new ObjectOutputStream( socket.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream ( socket.getInputStream() );
|
||||
|
||||
// send client file list
|
||||
out.writeObject( fileList );
|
||||
out.flush();
|
||||
// get update list
|
||||
FileListMessage updateList = (FileListMessage) in.readObject();
|
||||
expectedSize = updateList.getTotalSize();
|
||||
|
||||
// receive file updates
|
||||
File tmpPath = FileUtil.find(path);
|
||||
totalReceived = 0;
|
||||
for(FileInfo info : updateList.getFileList() ){
|
||||
// reading new file data
|
||||
File file = new File( tmpPath, info.getPath() );
|
||||
logger.fine("Updating file: "+file);
|
||||
if( !file.getParentFile().exists() && !file.getParentFile().mkdirs() ){
|
||||
throw new IOException("Unable to create folder: "+file.getParentFile());
|
||||
}
|
||||
File tmpFile = File.createTempFile(file.getName(), ".tmp", tmpPath);
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
byte[] buffer = new byte[socket.getReceiveBufferSize()];
|
||||
|
||||
long bytesReceived = 0;
|
||||
int byteRead = 0;
|
||||
long time = System.currentTimeMillis();
|
||||
long timeTotalRecived = 0;
|
||||
|
||||
while( bytesReceived < info.getSize() ) {
|
||||
byteRead = in.read(buffer);
|
||||
fileOut.write(buffer, 0, byteRead);
|
||||
bytesReceived += byteRead;
|
||||
|
||||
if(time+1000 < System.currentTimeMillis()){
|
||||
time = System.currentTimeMillis();
|
||||
speed = (int)(totalReceived - timeTotalRecived);
|
||||
timeTotalRecived = totalReceived;
|
||||
}
|
||||
|
||||
totalReceived += byteRead;
|
||||
if(progress != null) progress.progressUpdate(this, info, ((double)totalReceived/updateList.getTotalSize())*100);
|
||||
}
|
||||
fileOut.close();
|
||||
speed = 0;
|
||||
|
||||
// delete old file and replace whit new
|
||||
file.delete();
|
||||
if( !tmpFile.renameTo(file) ){
|
||||
throw new IOException("Can not move downloaded file: "+tmpFile.getAbsolutePath()+" to: "+file);
|
||||
}
|
||||
}
|
||||
}catch(ClassNotFoundException e){
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
|
||||
logger.info("Update done.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the speed of the transfer
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public long getSpeed(){
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of data received
|
||||
*
|
||||
* @return a long that represents bytes
|
||||
*/
|
||||
public long getTotalReceived(){
|
||||
return totalReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the expected total amount of data that will be received
|
||||
*
|
||||
* @return a long that represents bytes
|
||||
*/
|
||||
public long getTotalSize(){
|
||||
return expectedSize;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
15
src/zutil/net/update/UpdateConfigMessage.java
Normal file
15
src/zutil/net/update/UpdateConfigMessage.java
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A class that contains configuration information
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class UpdateConfigMessage implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected String hashAlgorithm;
|
||||
protected boolean compression;
|
||||
}
|
||||
101
src/zutil/net/update/UpdateServer.java
Normal file
101
src/zutil/net/update/UpdateServer.java
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.net.Socket;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServer;
|
||||
import zutil.net.threaded.ThreadedTCPNetworkServerThread;
|
||||
|
||||
public class UpdateServer extends ThreadedTCPNetworkServer{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
private FileListMessage fileList;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServer Thread
|
||||
*
|
||||
* @param path The path to sync the clients with
|
||||
*/
|
||||
public UpdateServer(int port, String path) throws Exception{
|
||||
super(port);
|
||||
fileList = new FileListMessage(path);
|
||||
MultiPrintStream.out.dump(fileList);
|
||||
|
||||
this.start();
|
||||
logger.info("Update Server Ready.");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ThreadedTCPNetworkServerThread getThreadInstance(Socket s) {
|
||||
return new UpdateServerThread(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the connecting clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class UpdateServerThread implements ThreadedTCPNetworkServerThread{
|
||||
private ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
private Socket socket;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServerThread
|
||||
*
|
||||
* @param client is the socket to the client
|
||||
*/
|
||||
public UpdateServerThread(Socket c){
|
||||
socket = c;
|
||||
try {
|
||||
out = new ObjectOutputStream( socket.getOutputStream());
|
||||
in = new ObjectInputStream ( socket.getInputStream() );
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void run(){
|
||||
try {
|
||||
logger.info("Client["+socket.getInetAddress()+"] connectiong...");
|
||||
// receive the clients file list
|
||||
FileListMessage clientFileList = (FileListMessage)in.readObject();
|
||||
MultiPrintStream.out.dump(clientFileList);
|
||||
FileListMessage diff = fileList.getDiff( clientFileList );
|
||||
MultiPrintStream.out.dump(diff);
|
||||
out.writeObject( diff );
|
||||
|
||||
logger.info("Updating client["+socket.getInetAddress()+"]...");
|
||||
for(FileInfo info : diff.getFileList()){
|
||||
// send file data
|
||||
FileInputStream input = new FileInputStream( info.getFile() );
|
||||
byte[] nextBytes = new byte[ socket.getSendBufferSize() ];
|
||||
int bytesRead = 0;
|
||||
while((bytesRead = input.read(nextBytes)) > 0){
|
||||
out.write(nextBytes,0,bytesRead);
|
||||
}
|
||||
out.flush();
|
||||
input.close();
|
||||
}
|
||||
|
||||
out.flush();
|
||||
socket.close();
|
||||
logger.info("Client["+socket.getInetAddress()+"] update done.");
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Update error Client["+socket.getInetAddress()+"].", e);
|
||||
} finally {
|
||||
logger.info("Client["+socket.getInetAddress()+"] disconnected.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
131
src/zutil/net/update/Zupdater.java
Normal file
131
src/zutil/net/update/Zupdater.java
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package zutil.net.update;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.GroupLayout;
|
||||
import javax.swing.GroupLayout.Alignment;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JProgressBar;
|
||||
import javax.swing.LayoutStyle.ComponentPlacement;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.JSeparator;
|
||||
import javax.swing.JButton;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.StringUtil;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Dialog.ModalExclusionType;
|
||||
|
||||
|
||||
public class Zupdater extends JFrame implements ProgressListener<UpdateClient, FileInfo>{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private JPanel contentPane;
|
||||
private JLabel lblSpeed;
|
||||
private JLabel lblFile;
|
||||
private JProgressBar progressBar;
|
||||
|
||||
/**
|
||||
* Launch the application.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
Zupdater frame = new Zupdater();
|
||||
frame.setVisible(true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public void progressUpdate(UpdateClient source, FileInfo info, double percent) {
|
||||
lblFile.setText( info.getPath() );
|
||||
progressBar.setValue( (int)percent );
|
||||
progressBar.setString( StringUtil.formatBytesToString( source.getTotalReceived() ) +
|
||||
" / "+StringUtil.formatBytesToString( source.getTotalSize() ));
|
||||
|
||||
lblSpeed.setText( StringUtil.formatBytesToString(((UpdateClient)source).getSpeed())+"/s" );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create the frame.
|
||||
*/
|
||||
public Zupdater() {
|
||||
setModalExclusionType(ModalExclusionType.APPLICATION_EXCLUDE);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
setResizable(false);
|
||||
setTitle("Updating...");
|
||||
setBounds(100, 100, 537, 124);
|
||||
contentPane = new JPanel();
|
||||
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
setContentPane(contentPane);
|
||||
|
||||
progressBar = new JProgressBar();
|
||||
progressBar.setString("2.5 MB / 5 MB");
|
||||
progressBar.setValue(50);
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setToolTipText("");
|
||||
|
||||
lblFile = new JLabel("apa.zip");
|
||||
|
||||
lblSpeed = new JLabel("200 kb/s");
|
||||
lblSpeed.setHorizontalAlignment(SwingConstants.RIGHT);
|
||||
|
||||
JSeparator separator = new JSeparator();
|
||||
|
||||
JButton btnCancel = new JButton("Cancel");
|
||||
btnCancel.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
|
||||
JLabel lblFile_1 = new JLabel("File:");
|
||||
GroupLayout gl_contentPane = new GroupLayout(contentPane);
|
||||
gl_contentPane.setHorizontalGroup(
|
||||
gl_contentPane.createParallelGroup(Alignment.TRAILING)
|
||||
.addGroup(gl_contentPane.createSequentialGroup()
|
||||
.addGap(10)
|
||||
.addComponent(progressBar, GroupLayout.DEFAULT_SIZE, 501, Short.MAX_VALUE)
|
||||
.addGap(10))
|
||||
.addComponent(separator, GroupLayout.DEFAULT_SIZE, 521, Short.MAX_VALUE)
|
||||
.addGroup(gl_contentPane.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(lblFile_1)
|
||||
.addPreferredGap(ComponentPlacement.RELATED)
|
||||
.addComponent(lblFile, GroupLayout.PREFERRED_SIZE, 331, GroupLayout.PREFERRED_SIZE)
|
||||
.addGap(60)
|
||||
.addComponent(lblSpeed, GroupLayout.DEFAULT_SIZE, 84, Short.MAX_VALUE)
|
||||
.addContainerGap())
|
||||
.addGroup(gl_contentPane.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(btnCancel))
|
||||
);
|
||||
gl_contentPane.setVerticalGroup(
|
||||
gl_contentPane.createParallelGroup(Alignment.LEADING)
|
||||
.addGroup(gl_contentPane.createSequentialGroup()
|
||||
.addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE)
|
||||
.addComponent(lblSpeed)
|
||||
.addComponent(lblFile)
|
||||
.addComponent(lblFile_1))
|
||||
.addPreferredGap(ComponentPlacement.RELATED)
|
||||
.addComponent(progressBar, GroupLayout.PREFERRED_SIZE, 25, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(ComponentPlacement.RELATED)
|
||||
.addComponent(separator, GroupLayout.PREFERRED_SIZE, GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(ComponentPlacement.RELATED)
|
||||
.addComponent(btnCancel)
|
||||
.addContainerGap(14, Short.MAX_VALUE))
|
||||
);
|
||||
contentPane.setLayout(gl_contentPane);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue