Many bugfixes and improvements
This commit is contained in:
parent
c3e3bbf787
commit
363e0c6cfc
52 changed files with 2021 additions and 982 deletions
|
|
@ -1,134 +0,0 @@
|
|||
package zutil.net;
|
||||
|
||||
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.ArrayList;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* This class connects to a update server and updates a path
|
||||
* with the servers
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class UpdateClient{
|
||||
private ArrayList<FileHash> clientFileList;
|
||||
private Socket socket;
|
||||
private String path;
|
||||
private ProgressListener progress;
|
||||
private int speed;
|
||||
private long totalReceived;
|
||||
|
||||
/**
|
||||
* 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{
|
||||
clientFileList = UpdateServer.getFileList(path);
|
||||
socket = new Socket(address, port);
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public void setProgressListener(ProgressListener p){
|
||||
progress = p;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the files
|
||||
*
|
||||
* @throws Exception
|
||||
*/
|
||||
public void update() throws Exception{
|
||||
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
|
||||
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
|
||||
|
||||
// send client file list
|
||||
out.writeObject(clientFileList);
|
||||
out.flush();
|
||||
|
||||
// receive file updates
|
||||
FileHash fileInfo = (FileHash)in.readObject();
|
||||
File tmpPath = FileUtil.find(path);
|
||||
while(!fileInfo.path.isEmpty()){
|
||||
MultiPrintStream.out.println("Updating: "+path+fileInfo.path);
|
||||
// reading new file data
|
||||
File file = new File(tmpPath.getAbsolutePath()+fileInfo.path);
|
||||
File tmpFile = File.createTempFile(file.getName(), ".tmp", tmpPath);
|
||||
tmpFile.getParentFile().mkdirs();
|
||||
tmpFile.deleteOnExit();
|
||||
|
||||
FileOutputStream fileOut = new FileOutputStream(tmpFile);
|
||||
byte[] buffer = new byte[socket.getReceiveBufferSize()];
|
||||
|
||||
int bytesReceived = 0;
|
||||
totalReceived = 0;
|
||||
long time = System.currentTimeMillis();
|
||||
long timeTotalRecived = 0;
|
||||
|
||||
while((bytesReceived = in.read(buffer)) > 0) {
|
||||
fileOut.write(buffer, 0, bytesReceived);
|
||||
|
||||
if(time+1000 < System.currentTimeMillis()){
|
||||
time = System.currentTimeMillis();
|
||||
speed = (int)(totalReceived - timeTotalRecived);
|
||||
timeTotalRecived = totalReceived;
|
||||
}
|
||||
|
||||
totalReceived += bytesReceived;
|
||||
if(progress != null) progress.progressUpdate(this, fileInfo, (double)totalReceived/fileInfo.size*100);
|
||||
}
|
||||
fileOut.close();
|
||||
speed = 0;
|
||||
|
||||
// delete old file and replace whit new
|
||||
file.delete();
|
||||
if(!tmpFile.renameTo(file)){
|
||||
throw new Exception("Cannot update file: "+file.getAbsolutePath());
|
||||
}
|
||||
// read new message
|
||||
fileInfo = (FileHash)in.readObject();
|
||||
}
|
||||
|
||||
MultiPrintStream.out.println("Update Done!!");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the speed of the transfer
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public int speed(){
|
||||
return speed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total amount of data received for the
|
||||
* current file
|
||||
*
|
||||
* @return The speed in bytes/s
|
||||
*/
|
||||
public long totalReceived(){
|
||||
return totalReceived;
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void close() throws IOException{
|
||||
socket.close();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,166 +0,0 @@
|
|||
package zutil.net;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.Serializable;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.URISyntaxException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import zutil.Hasher;
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
public class UpdateServer extends Thread{
|
||||
private ArrayList<FileHash> fileList;
|
||||
private ServerSocket server;
|
||||
private boolean close;
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServer Thread
|
||||
*
|
||||
* @param path The path to sync the clients with
|
||||
* @throws IOException
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public UpdateServer(int port, String path) throws Exception{
|
||||
fileList = getFileList(path);
|
||||
server = new ServerSocket(port);
|
||||
close = false;
|
||||
this.path = path;
|
||||
|
||||
this.start();
|
||||
MultiPrintStream.out.println("Update Server Online!!!");
|
||||
}
|
||||
|
||||
public void run(){
|
||||
while (!close){
|
||||
try {
|
||||
new UpdateServerThread(server.accept()).start();
|
||||
MultiPrintStream.out.println("Update Server: Client Connected!!!");
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles all the connecting clients
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class UpdateServerThread extends Thread{
|
||||
private ObjectOutputStream out;
|
||||
private ObjectInputStream in;
|
||||
private Socket client;
|
||||
|
||||
/**
|
||||
* Creates a UpdateServerThread
|
||||
* @param client The socket to the client
|
||||
* @throws IOException
|
||||
*/
|
||||
public UpdateServerThread(Socket c) throws IOException {
|
||||
client = c;
|
||||
out = new ObjectOutputStream(client.getOutputStream());
|
||||
in = new ObjectInputStream(client.getInputStream());
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void run(){
|
||||
try {
|
||||
// receive the clients filelist
|
||||
ArrayList<FileHash> clientFileList = (ArrayList<FileHash>)in.readObject();
|
||||
File tmpPath = FileUtil.find(path);
|
||||
|
||||
for(FileHash file : fileList){
|
||||
if(!clientFileList.contains(file)){
|
||||
// send new file to client
|
||||
out.writeObject(file);
|
||||
out.flush();
|
||||
|
||||
// send file data
|
||||
FileInputStream input = new FileInputStream(tmpPath.getAbsolutePath()+file.path);
|
||||
byte[] nextBytes = new byte[client.getSendBufferSize()];
|
||||
int bytesRead = 0;
|
||||
while((bytesRead = input.read(nextBytes)) > 0){
|
||||
out.write(nextBytes,0,bytesRead);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// send update done message
|
||||
out.writeObject(new FileHash("","",0));
|
||||
out.flush();
|
||||
|
||||
out.close();
|
||||
in.close();
|
||||
client.close();
|
||||
} catch (Exception e) {
|
||||
MultiPrintStream.out.println("Update Server: Client Error!!! "+e.getMessage());
|
||||
} finally {
|
||||
MultiPrintStream.out.println("Update Server: Client Update Done!!!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a ArrayList with all the files in the specified folder and there
|
||||
* MD5 hashes
|
||||
*
|
||||
* @param path The path to search
|
||||
* @return A ArrayList with all the files in the path
|
||||
* @throws URISyntaxException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws IOException
|
||||
*/
|
||||
public static ArrayList<FileHash> getFileList(String path) throws Exception{
|
||||
ArrayList<FileHash> fileHash = new ArrayList<FileHash>();
|
||||
|
||||
List<File> files = FileUtil.search(FileUtil.find(path));
|
||||
for(File file : files){
|
||||
fileHash.add(new FileHash(
|
||||
FileUtil.relativePath(file, path),
|
||||
Hasher.hash(file, "MD5"),
|
||||
file.length()));
|
||||
}
|
||||
|
||||
return fileHash;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This class is used to store the files
|
||||
* and there hashes
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
class FileHash implements Serializable{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public String path;
|
||||
public String hash;
|
||||
public long size;
|
||||
|
||||
public FileHash(String p, String h, long s){
|
||||
path = p;
|
||||
hash = h;
|
||||
size = s;
|
||||
}
|
||||
|
||||
public boolean equals(Object comp){
|
||||
FileHash tmp = (FileHash)comp;
|
||||
return path.equals(tmp.path) && hash.equals(tmp.hash);
|
||||
}
|
||||
|
||||
public String toString(){
|
||||
return path;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,232 +0,0 @@
|
|||
/*
|
||||
* Zupdater.java
|
||||
*
|
||||
* Created on den 27 juli 2008, 23:32
|
||||
*/
|
||||
|
||||
package zutil.net;
|
||||
|
||||
import java.awt.Dimension;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.StringUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class Zupdater extends javax.swing.JFrame implements ProgressListener{
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/** Creates new form Zupdater */
|
||||
public Zupdater() {
|
||||
super("Zupdater");
|
||||
initComponents();
|
||||
centerScreen();
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
public void centerScreen(){
|
||||
Dimension screen = getToolkit().getScreenSize();
|
||||
this.setBounds(
|
||||
(screen.width-getWidth())/2,
|
||||
(screen.height-getHeight())/2,
|
||||
getWidth(),
|
||||
getHeight() );
|
||||
}
|
||||
|
||||
public void progressUpdate(Object source, Object info, double percent) {
|
||||
if(info instanceof FileHash){
|
||||
FileHash fileHash = (FileHash) info;
|
||||
fileLabel.setText(fileHash.toString());
|
||||
fileProgressBar.setValue((int)percent);
|
||||
percentLabel.setText((int)percent+"%");
|
||||
|
||||
speedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).speed())+"/s");
|
||||
transferedLabel.setText(StringUtil.formatBytesToString(((UpdateClient)source).totalReceived())+
|
||||
" / "+StringUtil.formatBytesToString(fileHash.size));
|
||||
}
|
||||
}
|
||||
|
||||
/** This method is called from within the constructor to
|
||||
* initialize the form.
|
||||
* WARNING: Do NOT modify this code. The content of this method is
|
||||
* always regenerated by the Form Editor.
|
||||
*/
|
||||
// <editor-fold defaultstate="collapsed" desc="Generated Code">
|
||||
private void initComponents() {
|
||||
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
cancelButton = new javax.swing.JButton();
|
||||
jPanel1 = new javax.swing.JPanel();
|
||||
jLabel1 = new javax.swing.JLabel();
|
||||
totalProgressBar = new javax.swing.JProgressBar();
|
||||
jLabel2 = new javax.swing.JLabel();
|
||||
fileProgressBar = new javax.swing.JProgressBar();
|
||||
fileLabel = new javax.swing.JLabel();
|
||||
totalProgressLabel = new javax.swing.JLabel();
|
||||
speedLabel = new javax.swing.JLabel();
|
||||
percentLabel = new javax.swing.JLabel();
|
||||
transferedLabel = new javax.swing.JLabel();
|
||||
etaLabel = new javax.swing.JLabel();
|
||||
jLabel3 = new javax.swing.JLabel();
|
||||
|
||||
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
|
||||
|
||||
cancelButton.setText("Cancel");
|
||||
cancelButton.addActionListener(new java.awt.event.ActionListener() {
|
||||
public void actionPerformed(java.awt.event.ActionEvent evt) {
|
||||
cancel();
|
||||
}
|
||||
});
|
||||
|
||||
jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Update"));
|
||||
|
||||
jLabel1.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel1.setText("Total Progress:");
|
||||
|
||||
totalProgressBar.setIndeterminate(true);
|
||||
|
||||
jLabel2.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel2.setText("File: ");
|
||||
|
||||
fileLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
fileLabel.setText("file");
|
||||
|
||||
totalProgressLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
totalProgressLabel.setText("totalProgress");
|
||||
|
||||
speedLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
speedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
speedLabel.setText("speed");
|
||||
|
||||
percentLabel.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
percentLabel.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
|
||||
percentLabel.setText("0%");
|
||||
|
||||
transferedLabel.setFont(new java.awt.Font("Tahoma", 2, 11));
|
||||
transferedLabel.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
|
||||
transferedLabel.setText("transfer");
|
||||
|
||||
etaLabel.setFont(new java.awt.Font("Tahoma", 0, 11));
|
||||
etaLabel.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
|
||||
etaLabel.setText("eta");
|
||||
|
||||
jLabel3.setFont(new java.awt.Font("Tahoma", 1, 11));
|
||||
jLabel3.setText("ETA:");
|
||||
|
||||
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
|
||||
jPanel1.setLayout(jPanel1Layout);
|
||||
jPanel1Layout.setHorizontalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(jLabel1)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 464, Short.MAX_VALUE))
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.DEFAULT_SIZE, 555, Short.MAX_VALUE)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(jLabel3))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(etaLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 175, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(percentLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(transferedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 207, Short.MAX_VALUE))
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addComponent(fileLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 399, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(speedLabel, javax.swing.GroupLayout.DEFAULT_SIZE, 119, Short.MAX_VALUE)))))
|
||||
.addContainerGap())
|
||||
);
|
||||
jPanel1Layout.setVerticalGroup(
|
||||
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(jPanel1Layout.createSequentialGroup()
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel1)
|
||||
.addComponent(totalProgressLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(totalProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel2)
|
||||
.addComponent(fileLabel)
|
||||
.addComponent(speedLabel))
|
||||
.addGap(6, 6, 6)
|
||||
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
|
||||
.addComponent(jLabel3)
|
||||
.addComponent(etaLabel)
|
||||
.addComponent(transferedLabel)
|
||||
.addComponent(percentLabel))
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(fileProgressBar, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
|
||||
getContentPane().setLayout(layout);
|
||||
layout.setHorizontalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.DEFAULT_SIZE, 587, Short.MAX_VALUE)
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(cancelButton, javax.swing.GroupLayout.Alignment.TRAILING))
|
||||
.addContainerGap())
|
||||
);
|
||||
layout.setVerticalGroup(
|
||||
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(layout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
|
||||
.addComponent(cancelButton)
|
||||
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
pack();
|
||||
}// </editor-fold>
|
||||
|
||||
public void cancel(){
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param args the command line arguments
|
||||
*/
|
||||
public static void main(String args[]) {
|
||||
java.awt.EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
new Zupdater().setVisible(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Variables declaration - do not modify
|
||||
private javax.swing.JButton cancelButton;
|
||||
private javax.swing.JLabel etaLabel;
|
||||
private javax.swing.JLabel fileLabel;
|
||||
private javax.swing.JProgressBar fileProgressBar;
|
||||
private javax.swing.JLabel jLabel1;
|
||||
private javax.swing.JLabel jLabel2;
|
||||
private javax.swing.JLabel jLabel3;
|
||||
private javax.swing.JPanel jPanel1;
|
||||
private javax.swing.JSeparator jSeparator1;
|
||||
private javax.swing.JLabel percentLabel;
|
||||
private javax.swing.JLabel speedLabel;
|
||||
private javax.swing.JProgressBar totalProgressBar;
|
||||
private javax.swing.JLabel totalProgressLabel;
|
||||
private javax.swing.JLabel transferedLabel;
|
||||
// End of variables declaration
|
||||
|
||||
}
|
||||
98
src/zutil/net/http/HttpClient.java
Normal file
98
src/zutil/net/http/HttpClient.java
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.Socket;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
||||
import zutil.net.http.HttpPrintStream.HttpMessageType;
|
||||
|
||||
/**
|
||||
* This class connects to a HTTP server and
|
||||
* parses the result
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class HttpClient {
|
||||
public static enum HttpRequestType{
|
||||
GET, POST
|
||||
}
|
||||
|
||||
private HttpURL url;
|
||||
private HttpRequestType type;
|
||||
private HashMap<String,String> headers;
|
||||
private HashMap<String,String> cookies;
|
||||
|
||||
|
||||
public static HttpClient POST(){
|
||||
return new HttpClient( HttpRequestType.POST );
|
||||
}
|
||||
|
||||
public static HttpClient GET(){
|
||||
return new HttpClient( HttpRequestType.GET );
|
||||
}
|
||||
|
||||
|
||||
private HttpClient(HttpRequestType type){
|
||||
this.type = type;
|
||||
headers = new HashMap<String,String>();
|
||||
cookies = new HashMap<String,String>();
|
||||
}
|
||||
|
||||
|
||||
public void setURL( URL url){
|
||||
this.url = new HttpURL( url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a parameter to the request
|
||||
*/
|
||||
public void setParameter( String key, String value ){
|
||||
url.setParameter(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a cookie to the request
|
||||
*/
|
||||
public void setCookie( String key, String value ){
|
||||
cookies.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a header value to the request
|
||||
*/
|
||||
public void setHeader( String key, String value ){
|
||||
headers.put(key, value);
|
||||
}
|
||||
|
||||
public HttpHeaderParser send() throws IOException{
|
||||
Socket conn = new Socket( url.getHost(), url.getPort());
|
||||
|
||||
// Request
|
||||
HttpPrintStream request = new HttpPrintStream( conn.getOutputStream(), HttpMessageType.REQUEST );
|
||||
request.setRequestType( type.toString() );
|
||||
request.setRequestURL( url.getHttpURL() );
|
||||
request.setHeaders( headers );
|
||||
request.setCookies( cookies );
|
||||
|
||||
if( type == HttpRequestType.POST ){
|
||||
String data = url.getParameterString();
|
||||
request.setHeader("Content-Length", data);
|
||||
request.println();
|
||||
request.print( data );
|
||||
|
||||
}
|
||||
else
|
||||
request.println("");
|
||||
request.flush();
|
||||
|
||||
// Response
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
|
||||
HttpHeaderParser response = new HttpHeaderParser( in );
|
||||
conn.close();
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
|
@ -6,7 +6,7 @@ import java.util.HashMap;
|
|||
import java.util.Scanner;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class HTTPHeaderParser {
|
||||
public class HttpHeaderParser {
|
||||
// Some Cached regex's
|
||||
private static final Pattern colonPattern = Pattern.compile(":");
|
||||
private static final Pattern equalPattern = Pattern.compile("=");
|
||||
|
|
@ -30,7 +30,7 @@ public class HTTPHeaderParser {
|
|||
* @param in is the stream
|
||||
* @throws IOException
|
||||
*/
|
||||
public HTTPHeaderParser(BufferedReader in) throws IOException{
|
||||
public HttpHeaderParser(BufferedReader in) throws IOException{
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
|
@ -50,7 +50,7 @@ public class HTTPHeaderParser {
|
|||
*
|
||||
* @param in is the string
|
||||
*/
|
||||
public HTTPHeaderParser(String in){
|
||||
public HttpHeaderParser(String in){
|
||||
url_attr = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
|
|
@ -92,7 +92,7 @@ public class HTTPHeaderParser {
|
|||
if(index > -1){
|
||||
url = line.substring(0, index );
|
||||
line = line.substring( index+1, line.length());
|
||||
parseUrlAttributes(line, url_attr);
|
||||
parseURLParameters(line, url_attr);
|
||||
}
|
||||
else{
|
||||
url = line;
|
||||
|
|
@ -108,9 +108,9 @@ public class HTTPHeaderParser {
|
|||
*
|
||||
* @param attributes is the String containing all the attributes
|
||||
*/
|
||||
public static HashMap<String, String> parseUrlAttributes( String attributes ){
|
||||
public static HashMap<String, String> parseURLParameters( String attributes ){
|
||||
HashMap<String, String> map = new HashMap<String, String>();
|
||||
parseUrlAttributes(attributes, map);
|
||||
parseURLParameters(attributes, map);
|
||||
return map;
|
||||
}
|
||||
|
||||
|
|
@ -121,7 +121,7 @@ public class HTTPHeaderParser {
|
|||
* @param attributes is the String containing all the attributes
|
||||
* @param map is the HashMap to put all the values into
|
||||
*/
|
||||
public static void parseUrlAttributes(String attributes, HashMap<String, String> map){
|
||||
public static void parseURLParameters(String attributes, HashMap<String, String> map){
|
||||
String[] tmp;
|
||||
// get the variables
|
||||
String[] data = andPattern.split( attributes );
|
||||
|
|
@ -13,13 +13,13 @@ import java.util.HashMap;
|
|||
*/
|
||||
public class HttpPrintStream extends PrintStream{
|
||||
// Defines the type of message
|
||||
public enum HTTPMessageType{
|
||||
public enum HttpMessageType{
|
||||
REQUEST,
|
||||
RESPONSE
|
||||
}
|
||||
|
||||
// This defines the type of message that will be generated
|
||||
private HTTPMessageType message_type;
|
||||
private HttpMessageType message_type;
|
||||
// The status code of the message, ONLY for response
|
||||
private Integer res_status_code;
|
||||
// The request type of the message ONLY for request
|
||||
|
|
@ -27,9 +27,9 @@ public class HttpPrintStream extends PrintStream{
|
|||
// The requesting url ONLY for request
|
||||
private String req_url;
|
||||
// An Map of all the header values
|
||||
private HashMap<String, String> header;
|
||||
private HashMap<String, String> headers;
|
||||
// An Map of all the cookies
|
||||
private HashMap<String, String> cookie;
|
||||
private HashMap<String, String> cookies;
|
||||
// The buffered header
|
||||
private StringBuffer buffer;
|
||||
// If the header buffering is enabled
|
||||
|
|
@ -42,7 +42,7 @@ public class HttpPrintStream extends PrintStream{
|
|||
* @param out is the OutputStream to send the message
|
||||
*/
|
||||
public HttpPrintStream(OutputStream out) {
|
||||
this( out, HTTPMessageType.RESPONSE );
|
||||
this( out, HttpMessageType.RESPONSE );
|
||||
}
|
||||
/**
|
||||
* Creates an new instance of HttpPrintStream with
|
||||
|
|
@ -51,13 +51,13 @@ public class HttpPrintStream extends PrintStream{
|
|||
* @param out is the OutputStream to send the message
|
||||
* @param type is the type of message
|
||||
*/
|
||||
public HttpPrintStream(OutputStream out, HTTPMessageType type) {
|
||||
public HttpPrintStream(OutputStream out, HttpMessageType type) {
|
||||
super(out);
|
||||
|
||||
this.message_type = type;
|
||||
res_status_code = 0;
|
||||
header = new HashMap<String, String>();
|
||||
cookie = new HashMap<String, String>();
|
||||
headers = new HashMap<String, String>();
|
||||
cookies = new HashMap<String, String>();
|
||||
buffer = new StringBuffer();
|
||||
buffer_enabled = false;
|
||||
}
|
||||
|
|
@ -84,9 +84,9 @@ public class HttpPrintStream extends PrintStream{
|
|||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void setCookie(String key, String value) throws RuntimeException{
|
||||
if(cookie == null)
|
||||
if(cookies == null)
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
cookie.put(key, value);
|
||||
cookies.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -97,9 +97,9 @@ public class HttpPrintStream extends PrintStream{
|
|||
* @throws Exception Throws exception if the header has already been sent
|
||||
*/
|
||||
public void setHeader(String key, String value) throws RuntimeException{
|
||||
if(header == null)
|
||||
if(headers == null)
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
header.put(key, value);
|
||||
headers.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -111,7 +111,7 @@ public class HttpPrintStream extends PrintStream{
|
|||
public void setStatusCode(int code) throws RuntimeException{
|
||||
if( res_status_code == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.RESPONSE )
|
||||
if( message_type != HttpMessageType.RESPONSE )
|
||||
throw new RuntimeException("Status Code is only available in HTTP RESPONSE!!!");
|
||||
res_status_code = code;
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ public class HttpPrintStream extends PrintStream{
|
|||
public void setRequestType(String req_type) throws RuntimeException{
|
||||
if( req_type == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.REQUEST )
|
||||
if( message_type != HttpMessageType.REQUEST )
|
||||
throw new RuntimeException("Request Message Type is only available in HTTP REQUEST!!!");
|
||||
this.req_type = req_type;
|
||||
}
|
||||
|
|
@ -138,11 +138,18 @@ public class HttpPrintStream extends PrintStream{
|
|||
public void setRequestURL(String req_url) throws RuntimeException{
|
||||
if( req_url == null )
|
||||
throw new RuntimeException("Header already sent!!!");
|
||||
if( message_type != HTTPMessageType.REQUEST )
|
||||
if( message_type != HttpMessageType.REQUEST )
|
||||
throw new RuntimeException("Request URL is only available in HTTP REQUEST!!!");
|
||||
this.req_url = req_url;
|
||||
}
|
||||
|
||||
protected void setHeaders( HashMap<String,String> map ){
|
||||
headers = map;
|
||||
}
|
||||
protected void setCookies( HashMap<String,String> map ){
|
||||
cookies = map;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints with a new line
|
||||
*/
|
||||
|
|
@ -166,40 +173,40 @@ public class HttpPrintStream extends PrintStream{
|
|||
}
|
||||
else{
|
||||
if(res_status_code != null){
|
||||
if( message_type==HTTPMessageType.REQUEST )
|
||||
super.print(req_type+" "+req_url+" HTTP/1.1");
|
||||
if( message_type==HttpMessageType.REQUEST )
|
||||
super.print(req_type+" "+req_url+" HTTP/1.0");
|
||||
else
|
||||
super.print("HTTP/1.1 "+res_status_code+" "+getStatusString(res_status_code));
|
||||
super.print("HTTP/1.0 "+res_status_code+" "+getStatusString(res_status_code));
|
||||
super.println();
|
||||
res_status_code = null;
|
||||
req_type = null;
|
||||
req_url = null;
|
||||
}
|
||||
if(header != null){
|
||||
for(String key : header.keySet()){
|
||||
super.print(key+": "+header.get(key));
|
||||
if(headers != null){
|
||||
for(String key : headers.keySet()){
|
||||
super.print(key+": "+headers.get(key));
|
||||
super.println();
|
||||
}
|
||||
header = null;
|
||||
headers = null;
|
||||
}
|
||||
if(cookie != null){
|
||||
if( !cookie.isEmpty() ){
|
||||
if( message_type==HTTPMessageType.REQUEST ){
|
||||
if(cookies != null){
|
||||
if( !cookies.isEmpty() ){
|
||||
if( message_type==HttpMessageType.REQUEST ){
|
||||
super.print("Cookie: ");
|
||||
for(String key : cookie.keySet()){
|
||||
super.print(key+"="+cookie.get(key)+"; ");
|
||||
for(String key : cookies.keySet()){
|
||||
super.print(key+"="+cookies.get(key)+"; ");
|
||||
}
|
||||
super.println();
|
||||
}
|
||||
else{
|
||||
for(String key : cookie.keySet()){
|
||||
super.print("Set-Cookie: "+key+"="+cookie.get(key)+";");
|
||||
for(String key : cookies.keySet()){
|
||||
super.print("Set-Cookie: "+key+"="+cookies.get(key)+";");
|
||||
super.println();
|
||||
}
|
||||
}
|
||||
}
|
||||
super.println();
|
||||
cookie = null;
|
||||
cookies = null;
|
||||
}
|
||||
super.print(s);
|
||||
}
|
||||
|
|
@ -215,7 +222,7 @@ public class HttpPrintStream extends PrintStream{
|
|||
buffer.delete(0, buffer.length());
|
||||
buffer_enabled = true;
|
||||
}
|
||||
else if(res_status_code != null || header != null || cookie != null){
|
||||
else if(res_status_code != null || headers != null || cookies != null){
|
||||
printOrBuffer("");
|
||||
}
|
||||
super.flush();
|
||||
|
|
|
|||
|
|
@ -151,7 +151,7 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
|||
try {
|
||||
logger.finer("Reciving Http Request!!!");
|
||||
|
||||
HTTPHeaderParser parser = new HTTPHeaderParser(in);
|
||||
HttpHeaderParser parser = new HttpHeaderParser(in);
|
||||
logger.finest(parser.toString());
|
||||
client_info = parser.getHeaders();
|
||||
request = parser.getURLAttributes();
|
||||
|
|
@ -173,7 +173,7 @@ public class HttpServer extends ThreadedTCPNetworkServer{
|
|||
tmp = parser.getHeader("Content-Type");
|
||||
if( tmp.contains("application/x-www-form-urlencoded") ){
|
||||
// get the variables
|
||||
HTTPHeaderParser.parseUrlAttributes( tmpb.toString(), request );
|
||||
HttpHeaderParser.parseURLParameters( tmpb.toString(), request );
|
||||
}
|
||||
else if( tmp.contains("application/soap+xml" ) ||
|
||||
tmp.contains("text/xml") ||
|
||||
|
|
|
|||
139
src/zutil/net/http/HttpURL.java
Normal file
139
src/zutil/net/http/HttpURL.java
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
package zutil.net.http;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* Handles URLs in the HTTP protocol
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class HttpURL {
|
||||
public static final String PROTOCOL_SEPARATOR = "://";
|
||||
public static final String PORT_SEPARATOR = ":";
|
||||
public static final String PATH_SEPARATOR = "/";
|
||||
public static final String PARAMETER_SEPARATOR = "?";
|
||||
public static final String ANCHOR_SEPARATOR = "#";
|
||||
|
||||
private String protocol = "";
|
||||
private String host = "127.0.0.1";
|
||||
private int port = -1;
|
||||
private String path;
|
||||
private String anchor;
|
||||
|
||||
private HashMap<String,String> parameters = new HashMap<String,String>();
|
||||
|
||||
|
||||
public HttpURL(){}
|
||||
|
||||
public HttpURL( URL url ){
|
||||
this.setProtocol( url.getProtocol() );
|
||||
this.setHost( url.getHost() );
|
||||
this.setPort( url.getPort() );
|
||||
this.setPath( url.getPath() );
|
||||
}
|
||||
|
||||
|
||||
public String getProtocol( ){
|
||||
return protocol;
|
||||
}
|
||||
public String getHost( ){
|
||||
return host;
|
||||
}
|
||||
public int getPort( ){
|
||||
return port;
|
||||
}
|
||||
public String getPath( ){
|
||||
return path;
|
||||
}
|
||||
public String getAnchor( ){
|
||||
return anchor;
|
||||
}
|
||||
|
||||
public void setProtocol( String prot ){
|
||||
this.protocol = prot;
|
||||
}
|
||||
public void setHost( String host ){
|
||||
this.host = host;
|
||||
}
|
||||
public void setPort( int port ){
|
||||
this.port = port;
|
||||
}
|
||||
public void setPath( String path ){
|
||||
if( path.length() >= 1 && !path.startsWith(PATH_SEPARATOR))
|
||||
path = PATH_SEPARATOR + path;
|
||||
this.path = path;
|
||||
}
|
||||
public void setAnchor( String anch ){
|
||||
this.anchor = anch;
|
||||
}
|
||||
public void setParameter( String key, String value ){
|
||||
this.parameters.put(key, value);
|
||||
}
|
||||
|
||||
protected void setParameters( HashMap<String,String> pars ){
|
||||
this.parameters = pars;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the parameter string in a URL.
|
||||
*
|
||||
* e.g.
|
||||
* "key=value&key2=value&..."
|
||||
*/
|
||||
public String getParameterString(){
|
||||
StringBuilder param = new StringBuilder();
|
||||
for(String key : parameters.keySet()){
|
||||
param.append(key);
|
||||
param.append('=');
|
||||
param.append( parameters.get(key) );
|
||||
param.append('&');
|
||||
}
|
||||
if( param.length() > 0 )
|
||||
param.deleteCharAt( param.length()-1 );
|
||||
return param.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a path that are used in the HTTP header
|
||||
*/
|
||||
public String getHttpURL(){
|
||||
StringBuilder url = new StringBuilder();
|
||||
url.append( path );
|
||||
if( !parameters.isEmpty() )
|
||||
url.append( PARAMETER_SEPARATOR ).append( getParameterString() );
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a full URL
|
||||
*/
|
||||
public String getURL(){
|
||||
return toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates the whole URL
|
||||
*/
|
||||
public String toString(){
|
||||
StringBuilder url = new StringBuilder();
|
||||
url.append( protocol );
|
||||
url.append( PROTOCOL_SEPARATOR );
|
||||
url.append( host );
|
||||
if( port > 0 )
|
||||
url.append( PORT_SEPARATOR ).append( port );
|
||||
|
||||
if( path != null )
|
||||
url.append( path );
|
||||
else
|
||||
url.append( PATH_SEPARATOR );
|
||||
|
||||
if( !parameters.isEmpty() )
|
||||
url.append( PARAMETER_SEPARATOR ).append( getParameterString() );
|
||||
if( anchor != null )
|
||||
url.append( ANCHOR_SEPARATOR ).append( anchor );
|
||||
|
||||
return url.toString();
|
||||
}
|
||||
}
|
||||
|
|
@ -13,7 +13,7 @@ import java.util.List;
|
|||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import zutil.ProgressListener;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
|
||||
/**
|
||||
* Parses a multipart/form-data http request,
|
||||
|
|
@ -35,11 +35,11 @@ public class MultipartParser {
|
|||
private BufferedReader in;
|
||||
|
||||
/** This is the listener that will listen on the progress */
|
||||
private ProgressListener<MultipartField> listener;
|
||||
private ProgressListener<MultipartParser,MultipartField> listener;
|
||||
|
||||
|
||||
|
||||
public MultipartParser(BufferedReader in, HTTPHeaderParser header){
|
||||
public MultipartParser(BufferedReader in, HttpHeaderParser header){
|
||||
this.in = in;
|
||||
|
||||
String cotype = header.getHeader("Content-type");
|
||||
|
|
@ -69,7 +69,7 @@ public class MultipartParser {
|
|||
/**
|
||||
* @param listener is the listener that will be called for progress
|
||||
*/
|
||||
public void setListener(ProgressListener<MultipartField> listener){
|
||||
public void setListener(ProgressListener<MultipartParser,MultipartField> listener){
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import java.util.logging.Logger;
|
|||
|
||||
import zutil.io.StringOutputStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.threaded.ThreadedUDPNetwork;
|
||||
import zutil.net.threaded.ThreadedUDPNetworkThread;
|
||||
|
|
@ -75,7 +75,7 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
|
|||
|
||||
// Generate an SSDP discover message
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
|
||||
http.setRequestType("M-SEARCH");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Host", SSDPServer.SSDP_MULTICAST_ADDR+":"+SSDPServer.SSDP_PORT );
|
||||
|
|
@ -150,7 +150,7 @@ public class SSDPClient extends ThreadedUDPNetwork implements ThreadedUDPNetwork
|
|||
* Location: http://localhost:80
|
||||
*/
|
||||
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network) {
|
||||
HTTPHeaderParser header = new HTTPHeaderParser( new String( packet.getData() ) );
|
||||
HttpHeaderParser header = new HttpHeaderParser( new String( packet.getData() ) );
|
||||
logger.log(Level.FINEST, "*********** Recived\n"+header);
|
||||
|
||||
String usn = header.getHeader("USN");
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import java.util.logging.Logger;
|
|||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.StringOutputStream;
|
||||
import zutil.log.LogUtil;
|
||||
import zutil.net.http.HTTPHeaderParser;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
import zutil.net.http.HttpPrintStream;
|
||||
import zutil.net.threaded.ThreadedUDPNetworkThread;
|
||||
import zutil.net.threaded.ThreadedUDPNetwork;
|
||||
|
|
@ -155,7 +155,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
|
|||
try {
|
||||
String msg = new String( packet.getData() );
|
||||
|
||||
HTTPHeaderParser header = new HTTPHeaderParser( msg );
|
||||
HttpHeaderParser header = new HttpHeaderParser( msg );
|
||||
logger.log(Level.FINEST, "**** Received:\n"+header);
|
||||
|
||||
// ******* Respond
|
||||
|
|
@ -233,7 +233,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
|
|||
try {
|
||||
// Generate the SSDP response
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
|
||||
http.setRequestType("NOTIFY");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Server", SERVER_INFO );
|
||||
|
|
@ -284,7 +284,7 @@ public class SSDPServer extends ThreadedUDPNetwork implements ThreadedUDPNetwork
|
|||
try {
|
||||
// Generate the SSDP response
|
||||
StringOutputStream msg = new StringOutputStream();
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HTTPMessageType.REQUEST );
|
||||
HttpPrintStream http = new HttpPrintStream( msg, HttpPrintStream.HttpMessageType.REQUEST );
|
||||
http.setRequestType("NOTIFY");
|
||||
http.setRequestURL("*");
|
||||
http.setHeader("Server", SERVER_INFO );
|
||||
|
|
|
|||
|
|
@ -8,19 +8,22 @@ import java.security.KeyStoreException;
|
|||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.net.ssl.SSLServerSocketFactory;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.log.LogUtil;
|
||||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
* A simple network server that handles TCP communication
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public abstract class ThreadedTCPNetworkServer extends Thread{
|
||||
public static final Logger logger = LogUtil.getLogger();
|
||||
|
||||
public final int port;
|
||||
private File keyStore;
|
||||
private String keyStorePass;
|
||||
|
|
@ -28,7 +31,7 @@ public abstract class ThreadedTCPNetworkServer extends Thread{
|
|||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
* @param port The port that the server should listen to
|
||||
*/
|
||||
public ThreadedTCPNetworkServer(int port){
|
||||
this(port, null, null);
|
||||
|
|
@ -36,9 +39,9 @@ public abstract class ThreadedTCPNetworkServer extends Thread{
|
|||
/**
|
||||
* Creates a new instance of the sever
|
||||
*
|
||||
* @param port The port that the server should listen to
|
||||
* @param sslCert If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param sslCert If this is not null then the server will use a SSL connection with the given certificate
|
||||
* @param port The port that the server should listen to
|
||||
* @param sslCert If this is not null then the server will use SSL connection with this keyStore file path
|
||||
* @param sslCert If this is not null then the server will use a SSL connection with the given certificate
|
||||
*/
|
||||
public ThreadedTCPNetworkServer(int port, File keyStore, String keyStorePass){
|
||||
this.port = port;
|
||||
|
|
@ -64,18 +67,18 @@ public abstract class ThreadedTCPNetworkServer extends Thread{
|
|||
if( t!=null )
|
||||
new Thread( t ).start();
|
||||
else{
|
||||
MultiPrintStream.out.println("Unable to instantiate ThreadedTCPNetworkServerThread, closing connection!");
|
||||
logger.severe("Unable to instantiate ThreadedTCPNetworkServerThread, closing connection!");
|
||||
s.close();
|
||||
}
|
||||
}
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace( MultiPrintStream.out );
|
||||
logger.log(Level.SEVERE, null, e);
|
||||
}
|
||||
|
||||
if( ss!=null ){
|
||||
try{
|
||||
ss.close();
|
||||
}catch(IOException e){ e.printStackTrace( MultiPrintStream.out ); }
|
||||
}catch(IOException e){ logger.log(Level.SEVERE, null, e); }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,17 +87,16 @@ public abstract class ThreadedTCPNetworkServer extends Thread{
|
|||
* that will handle the newly made connection, if an null value is returned
|
||||
* then the ThreadedTCPNetworkServer will close the new connection.
|
||||
*
|
||||
* @param s is an new connection to an host
|
||||
* @return a new instance of an thread or null
|
||||
* @param s is an new connection to an host
|
||||
* @return a new instance of an thread or null
|
||||
*/
|
||||
protected abstract ThreadedTCPNetworkServerThread getThreadInstance( Socket s );
|
||||
|
||||
/**
|
||||
* Initiates a SSLServerSocket
|
||||
*
|
||||
* @param port The port to listen to
|
||||
* @return The SSLServerSocket
|
||||
* @throws IOException
|
||||
* @param port The port to listen to
|
||||
* @return The SSLServerSocket
|
||||
*/
|
||||
private ServerSocket initSSL(int port) throws IOException{
|
||||
SSLServerSocketFactory sslserversocketfactory =
|
||||
|
|
@ -106,7 +108,7 @@ public abstract class ThreadedTCPNetworkServer extends Thread{
|
|||
/**
|
||||
* Registers the given cert file to the KeyStore
|
||||
*
|
||||
* @param certFile The cert file
|
||||
* @param certFile The cert file
|
||||
*/
|
||||
protected void registerCertificate(File keyStore, String keyStorePass) throws CertificateException, IOException, KeyStoreException, NoSuchProviderException, NoSuchAlgorithmException{
|
||||
System.setProperty("javax.net.ssl.keyStore", keyStore.getAbsolutePath());
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
package zutil.net.threaded;
|
||||
|
||||
/**
|
||||
* The class that will handle the TCP connection will incclude
|
||||
* The class that will handle the a TCP connection will include
|
||||
* this interface
|
||||
*
|
||||
* @author Ziver
|
||||
|
|
|
|||
|
|
@ -10,8 +10,7 @@ import java.net.SocketException;
|
|||
|
||||
|
||||
/**
|
||||
* A simple web server that handles both cookies and
|
||||
* sessions for all the clients
|
||||
* * A simple network server that handles UDP communication
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import java.net.DatagramPacket;
|
|||
|
||||
/**
|
||||
* This interface is for processing received packets
|
||||
* from the TNetworkUDPServer
|
||||
* from the ThreadedUDPNetworkServer
|
||||
*
|
||||
* @author Ziver
|
||||
*
|
||||
|
|
@ -14,8 +14,8 @@ public interface ThreadedUDPNetworkThread extends Runnable{
|
|||
/**
|
||||
* Packet will be processed in this method
|
||||
*
|
||||
* @param packet is the received packet
|
||||
* @param network is the network class that received the packet
|
||||
* @param packet is the received packet
|
||||
* @param network is the network class that received the packet
|
||||
*/
|
||||
public void receivedPacket(DatagramPacket packet, ThreadedUDPNetwork network);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,102 +0,0 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
public class Torrent {
|
||||
// Name of the torrent
|
||||
private String name;
|
||||
// Comment
|
||||
private String comment;
|
||||
// Creation date as unix timestamp
|
||||
private long date;
|
||||
// Files in the torrent
|
||||
private ArrayList<String> file_list;
|
||||
// Size of of the full torrent (after download)
|
||||
private long size;
|
||||
// Signature of the software which created the torrent
|
||||
private String created_by;
|
||||
// tracker (the tracker the torrent has been received from)
|
||||
private String main_tracker;
|
||||
// List of known trackers for the torrent
|
||||
private ArrayList<String> tracker_list;
|
||||
private HashMap<String,Object> info_hash;
|
||||
// Torrent is marked as 'private'.
|
||||
private boolean is_private;
|
||||
|
||||
public Torrent(File torrent) throws IOException{
|
||||
this(FileUtil.getFileContent( torrent ));
|
||||
}
|
||||
|
||||
public Torrent(String data){
|
||||
reset();
|
||||
decode(data);
|
||||
}
|
||||
|
||||
private void reset(){
|
||||
// Reset
|
||||
name = "";
|
||||
comment = "";
|
||||
date = 0;
|
||||
file_list = new ArrayList<String>();
|
||||
size = 0;
|
||||
created_by = "";
|
||||
main_tracker = "";
|
||||
tracker_list = new ArrayList<String>();
|
||||
info_hash = new HashMap<String,Object>();
|
||||
is_private = false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void decode(String data){
|
||||
HashMap<?,?> dataMap = (HashMap<?,?>)TorrentParser.decode(data);
|
||||
|
||||
name = (String)dataMap.get("name");
|
||||
comment = (String)dataMap.get("comment");
|
||||
date = (Long)dataMap.get("creation date");
|
||||
file_list = new ArrayList<String>();
|
||||
size = (Long)dataMap.get("length");
|
||||
created_by = (String)dataMap.get("created by");
|
||||
main_tracker = (String)dataMap.get("announce");
|
||||
tracker_list = (ArrayList<String>)dataMap.get("announce-list");
|
||||
info_hash = (HashMap<String, Object>)dataMap.get("info");
|
||||
is_private = (((Integer)dataMap.get("private")) != 0);
|
||||
}
|
||||
|
||||
// ************** GETTER **************
|
||||
public String getName(){
|
||||
return name;
|
||||
}
|
||||
public String getComments(){
|
||||
return comment;
|
||||
}
|
||||
public long getDate(){
|
||||
return date;
|
||||
}
|
||||
public ArrayList<String> getFileList(){
|
||||
return file_list;
|
||||
}
|
||||
public long getSize(){
|
||||
return size;
|
||||
}
|
||||
public String getAuthor(){
|
||||
return created_by;
|
||||
}
|
||||
public String getMainTracker(){
|
||||
return main_tracker;
|
||||
}
|
||||
public ArrayList<String> getTrackerList(){
|
||||
return tracker_list;
|
||||
}
|
||||
public HashMap<String,Object> getInfoHash(){
|
||||
return info_hash;
|
||||
}
|
||||
public boolean isPrivate(){
|
||||
return is_private;
|
||||
}
|
||||
// ************************************
|
||||
}
|
||||
32
src/zutil/net/torrent/TorrentFile.java
Normal file
32
src/zutil/net/torrent/TorrentFile.java
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import zutil.Dumpable;
|
||||
|
||||
/**
|
||||
* This class represents a File for download
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class TorrentFile implements Dumpable{
|
||||
private String filename;
|
||||
private long length;
|
||||
|
||||
public TorrentFile(String filename, long length){
|
||||
this.filename = filename;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
|
||||
public String getFilename() {
|
||||
return filename;
|
||||
}
|
||||
public void setFilename(String filename) {
|
||||
this.filename = filename;
|
||||
}
|
||||
public long getLength() {
|
||||
return length;
|
||||
}
|
||||
public void setLength(long length) {
|
||||
this.length = length;
|
||||
}
|
||||
}
|
||||
165
src/zutil/net/torrent/TorrentMetainfo.java
Normal file
165
src/zutil/net/torrent/TorrentMetainfo.java
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
import zutil.parser.BEncodedParser;
|
||||
import zutil.parser.DataNode;
|
||||
|
||||
public class TorrentMetainfo {
|
||||
/** Comment (optional) **/
|
||||
private String comment;
|
||||
/** Signature of the software which created the torrent (optional) **/
|
||||
private String created_by;
|
||||
/** Creation date as Unix timestamp (optional) **/
|
||||
private long creation_date;
|
||||
/** The main Tracker (the tracker the torrent has been received from) **/
|
||||
private String announce;
|
||||
/** List of known trackers for the torrent (optional) **/
|
||||
private ArrayList<String> announce_list;
|
||||
/** The encoding of the Strings (optional) **/
|
||||
private String encoding;
|
||||
|
||||
/** Size of of the full torrent (after download) **/
|
||||
private long size;
|
||||
/** Number of bytes in each piece **/
|
||||
private long piece_length;
|
||||
/** String consisting of the concatenation of all 20-byte SHA1 hash values, one per piece **/
|
||||
private ArrayList<String> piece_hashes;
|
||||
/** Torrent is private. No other trackers allowed other then those listed. (optional) **/
|
||||
private boolean is_private;
|
||||
|
||||
// Files in the torrent
|
||||
private ArrayList<TorrentFile> file_list;
|
||||
|
||||
|
||||
|
||||
public TorrentMetainfo(File torrent) throws IOException{
|
||||
this(FileUtil.getFileContent( torrent ));
|
||||
}
|
||||
|
||||
public TorrentMetainfo(String data){
|
||||
decode(data);
|
||||
}
|
||||
|
||||
|
||||
private void decode(String data){
|
||||
DataNode metainfo = BEncodedParser.parse(data);
|
||||
|
||||
// Main values
|
||||
announce = metainfo.getString("announce");
|
||||
created_by = metainfo.getString("created by");
|
||||
comment = metainfo.getString("comment");
|
||||
encoding = metainfo.getString("encoding");
|
||||
if( metainfo.get("creation date") != null )
|
||||
creation_date = metainfo.getLong("creation date");
|
||||
if( metainfo.get("announce-list") != null ){
|
||||
DataNode tmp = metainfo.get("announce-list");
|
||||
announce_list = new ArrayList<String>();
|
||||
for( DataNode tracker : tmp )
|
||||
announce_list.add( tracker.getString() );
|
||||
}
|
||||
|
||||
// info data
|
||||
DataNode info = metainfo.get("info");
|
||||
String name = info.getString("name");
|
||||
piece_length = info.getLong("piece length");
|
||||
if( info.get("private") != null )
|
||||
is_private = (info.getInt("private") != 0);
|
||||
// Split the hashes
|
||||
String hashes = info.getString("pieces");
|
||||
piece_hashes = new ArrayList<String>();
|
||||
for(int i=0; i<hashes.length() ;i++){
|
||||
String hash = "";
|
||||
for(; i%20!=0 ;i++)
|
||||
hash += hashes.charAt(i);
|
||||
piece_hashes.add(hash);
|
||||
}
|
||||
|
||||
// File data
|
||||
file_list = new ArrayList<TorrentFile>();
|
||||
// Single-file torrent
|
||||
if( info.get("files") == null ){
|
||||
Long length = info.getLong("length");
|
||||
file_list.add( new TorrentFile(name, length) );
|
||||
}
|
||||
// Multi-file torrent
|
||||
else{
|
||||
DataNode files = info.get("files");
|
||||
for( DataNode file : files ){
|
||||
String filename = "";
|
||||
DataNode tmp = file.get("path");
|
||||
// File in subdir
|
||||
if( tmp.isList() ){
|
||||
Iterator<DataNode> it = tmp.iterator();
|
||||
while( it.hasNext() ){
|
||||
filename += it.next().getString();
|
||||
if(it.hasNext()) filename += File.separator;
|
||||
}
|
||||
}
|
||||
// File in root dir
|
||||
else
|
||||
filename = tmp.getString();
|
||||
Long length = file.getLong("length");
|
||||
filename = name + File.separator + filename;
|
||||
file_list.add( new TorrentFile(filename, length) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public String getComment() {
|
||||
return comment;
|
||||
}
|
||||
public String getCreated_by() {
|
||||
return created_by;
|
||||
}
|
||||
public long getCreation_date() {
|
||||
return creation_date;
|
||||
}
|
||||
public String getAnnounce() {
|
||||
return announce;
|
||||
}
|
||||
public ArrayList<String> getAnnounce_list() {
|
||||
return announce_list;
|
||||
}
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
public long getPiece_length() {
|
||||
return piece_length;
|
||||
}
|
||||
public ArrayList<String> getPiece_hashes() {
|
||||
return piece_hashes;
|
||||
}
|
||||
public boolean isIs_private() {
|
||||
return is_private;
|
||||
}
|
||||
public ArrayList<TorrentFile> getFile_list() {
|
||||
return file_list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Example use
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
TorrentMetainfo tmp = new TorrentMetainfo(FileUtil.find("C:\\Users\\Ziver\\Desktop\\test.torrent"));
|
||||
MultiPrintStream.out.dump(tmp);
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import zutil.io.MultiPrintStream;
|
||||
import zutil.io.file.FileUtil;
|
||||
|
||||
/**
|
||||
* http://wiki.theory.org/BitTorrentSpecification
|
||||
* @author Ziver
|
||||
*
|
||||
*/
|
||||
public class TorrentParser {
|
||||
/**
|
||||
* Example use
|
||||
*/
|
||||
public static void main(String[] args){
|
||||
try {
|
||||
String tmp = FileUtil.getFileContent(FileUtil.find("C:\\Users\\Ziver\\Desktop\\test.torrent"));
|
||||
MultiPrintStream.out.dump(TorrentParser.decode(tmp));
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the representation of the data in the BEncoded string
|
||||
*
|
||||
* @param data The data to be decoded
|
||||
* @return
|
||||
*/
|
||||
public static Object decode(String data){
|
||||
return decode_BEncoded(new StringBuffer(data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the representation of the data in the BEncoded string
|
||||
*
|
||||
* @param data The data to be decoded
|
||||
* @param index The index in data to start from
|
||||
* @return
|
||||
*/
|
||||
private static Object decode_BEncoded(StringBuffer data){
|
||||
String tmp;
|
||||
char c = ' ';
|
||||
|
||||
switch (data.charAt(0)) {
|
||||
/**
|
||||
* Integers are prefixed with an i and terminated by an e. For
|
||||
* example, 123 would bEcode to i123e, -3272002 would bEncode to
|
||||
* i-3272002e.
|
||||
*/
|
||||
case 'i':
|
||||
//System.out.println("Found Integer at "+index);
|
||||
data.deleteCharAt(0);
|
||||
tmp = data.substring(0, data.indexOf("e"));
|
||||
data.delete(0, tmp.length() + 1);
|
||||
//System.out.println(tmp);
|
||||
return new Long(tmp);
|
||||
/**
|
||||
* Lists are prefixed with a l and terminated by an e. The list
|
||||
* should contain a series of bEncoded elements. For example, the
|
||||
* list of strings ["Monduna", "Bit", "Torrents"] would bEncode to
|
||||
* l7:Monduna3:Bit8:Torrentse. The list [1, "Monduna", 3, ["Sub", "List"]]
|
||||
* would bEncode to li1e7:Mondunai3el3:Sub4:Listee
|
||||
*/
|
||||
case 'l':
|
||||
//System.out.println("Found List at "+index);
|
||||
data.deleteCharAt(0);
|
||||
LinkedList<Object> list = new LinkedList<Object>();
|
||||
c = data.charAt(0);
|
||||
while(c != 'e'){
|
||||
list.add(decode_BEncoded(data));
|
||||
c = data.charAt(0);
|
||||
}
|
||||
data.deleteCharAt(0);
|
||||
//MultiPrintStream.out.dump(list);
|
||||
if(list.size() == 1) return list.poll();
|
||||
else return list;
|
||||
/**
|
||||
* Dictionaries are prefixed with a d and terminated by an e. They
|
||||
* are similar to list, except that items are in key value pairs. The
|
||||
* dictionary {"key":"value", "Monduna":"com", "bit":"Torrents", "number":7}
|
||||
* would bEncode to d3:key5:value7:Monduna3:com3:bit:8:Torrents6:numberi7ee
|
||||
*/
|
||||
case 'd':
|
||||
//System.out.println("Found Dictionary at "+index);
|
||||
data.deleteCharAt(0);
|
||||
HashMap<Object,Object> map = new HashMap<Object,Object>();
|
||||
c = data.charAt(0);
|
||||
while(c != 'e'){
|
||||
Object tmp2 = decode_BEncoded(data);
|
||||
map.put(tmp2, decode_BEncoded(data));
|
||||
c = data.charAt(0);
|
||||
}
|
||||
data.deleteCharAt(0);
|
||||
//MultiPrintStream.out.dump(map);
|
||||
return map;
|
||||
/**
|
||||
* Strings are prefixed with their length followed by a colon.
|
||||
* For example, "Monduna" would bEncode to 7:Monduna and "BitTorrents"
|
||||
* would bEncode to 11:BitTorrents.
|
||||
*/
|
||||
default:
|
||||
//System.out.println("Found String at "+index);
|
||||
tmp = data.substring(0, data.indexOf(":"));
|
||||
int length = Integer.parseInt(tmp);
|
||||
data.delete(0, tmp.length()+1);
|
||||
String ret = data.substring(0, length);
|
||||
data.delete(0, length);
|
||||
//System.out.println(data.substring(i, i+length));
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
26
src/zutil/net/torrent/TorrentTracker.java
Normal file
26
src/zutil/net/torrent/TorrentTracker.java
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
package zutil.net.torrent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
|
||||
import zutil.net.http.HttpClient;
|
||||
import zutil.net.http.HttpHeaderParser;
|
||||
|
||||
/**
|
||||
* This tracker represents a tracker client
|
||||
* that connects to a tracker
|
||||
*
|
||||
* @author Ziver
|
||||
*/
|
||||
public class TorrentTracker {
|
||||
/** The address to the tracker **/
|
||||
private URL trackerURL;
|
||||
|
||||
|
||||
|
||||
private void update() throws IOException {
|
||||
HttpClient request = HttpClient.GET();
|
||||
request.setURL( trackerURL );
|
||||
HttpHeaderParser response = request.send();
|
||||
}
|
||||
}
|
||||
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