Many bugfixes and improvements

This commit is contained in:
Ziver Koc 2011-06-24 23:20:59 +00:00
parent c3e3bbf787
commit 363e0c6cfc
52 changed files with 2021 additions and 982 deletions

View 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;
}
}

View 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;
}
}

View 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();
}
}

View 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;
}

View 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.");
}
}
}
}

View 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);
}
}