Commit 0bd1506d authored by SHIVAM DIXIT's avatar SHIVAM DIXIT

First commit

parents
TEAM NAME - COT
PROJECT NAME - FSync
Roll No. Name git username Contribution
193050012 Shivam Dixit @shivamdixit sync algo, networking between client and server
203050060 Nilesh Tanwar @parsec client gui
203050105 Anmol Kalra @anmolkalra sync algo
203050109 Ankit Kumar @ankitkumar client gui
203050057 Sandeep Gupta @deeep database
Building and running server :
Go to 'Server' directory.
run : chmod 777 build.sh
run : ./build.sh
This will create 'Server.jar' and 'RunServer.sh' files.
Server needs a config file. A sample config file is given, you can edit it according to your setup.
Server config file has two fields :
PORT:<port number>
OPERATION_DIR:<working directory for server>
Trailing '/' in OPERATION_DIR is must.
For example : OPERATION_DIR:/path/to/dir is wrong.
OPERATION_DIR:/path/to/dir/ is right.
To run the server:
./RunServer.sh <configFilePath>
Example :
./RunServer.sh dir/config.txt
Building and running client :
Go to 'Client' directory.
run : chmod 777 build.sh
run : ./build.sh
This will create 'Client.jar' and 'RunClient.sh' files.
Client needs a config file. A sample config file is given, you can edit it according to your setup.
Client config file has three fields :
SERVER_ADDR:<server ip address>
SERVER_PORT:<server port>
OPERATION_DIR:<working directory for client>
Trailing '/' in OPERATION_DIR is must.
For example : OPERATION_DIR:/path/to/dir is wrong.
OPERATION_DIR:/path/to/dir/ is right.
To run the client:
./RunClient.sh <configFilePath>
Example :
./RunClient.sh dir/config.txt
References:
https://rsync.samba.org/
https://en.wikipedia.org/wiki/Rsync
https://www.javatpoint.com/PreparedStatement-interface
http://pages.cs.wisc.edu/~jignesh/cs564/notes/Doxygen.pdf
https://www.baeldung.com/java-create-jar
import java.util.Arrays;
/**
* Contains block id, weak hash and strong hash of the block.
*/
public class BlockSig {
int id;
int hash; // weak hash
byte md5[]; // strong hash
public BlockSig(int id, int hash, byte md5[]) {
this.id = id;
this.hash = hash;
this.md5 = md5;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof BlockSig))
return false;
return hash == ((BlockSig) obj).hash && Arrays.equals(md5, ((BlockSig) obj).md5);
}
@Override
public int hashCode() {
return hash * 31 + Arrays.hashCode(md5);
}
}
\ No newline at end of file
This diff is collapsed.
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
*
* Represents a network connection.
*
*/
public class Connection {
Socket socket;
DataInputStream dataIn;
DataOutputStream dataOut;
// all incoming msgs go to inQueue
// all msgs that are placed in outQueue are sent through this connection's
// socket
LinkedBlockingQueue<Msg> inQueue, outQueue;
Thread recThread, sendThread;
Object userData; // can be used to store arbitrary data
boolean isActive = false, keepRunning = false;
String userName = null;
File sigFile = null, deltaFile = null;
DataOutputStream sigFileOut = null, deltaFileOut = null;
/**
*
* @param socket : network socket for this connection
* @param inQueue : main thread's inbox, all incoming messages will be put in
* this queue
* @throws IOException
*/
public Connection(Socket socket, LinkedBlockingQueue<Msg> inQueue) throws IOException {
this.socket = socket;
dataIn = new DataInputStream(socket.getInputStream());
dataOut = new DataOutputStream(socket.getOutputStream());
this.inQueue = inQueue;
outQueue = new LinkedBlockingQueue<>();
}
/**
* Adds the given message to the send queue.
*
* @param msg : message to be sent
*/
public void send(Msg msg) {
outQueue.add(msg);
}
/**
* Send the whole file through this connection.
*
* @param f : file to be sent
* @param fileType : either SIGFILE or DELTAFILE
* @throws IOException
*/
public void sendFile(File f, int fileType) throws IOException {
DataInputStream din = new DataInputStream(new FileInputStream(f));
long size = f.length();
byte b[] = new byte[1024];
while (size > 0) {
int read = din.read(b);
size -= read;
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(fileType);
if (size == 0)
dout.writeInt(1);
else
dout.writeInt(0);
dout.writeInt(read);
dout.write(b, 0, read);
m.prepare();
send(m);
}
din.close();
}
/**
* Creates send and receive threads. Send thread sends all the messages that it
* receives from other parts of the program through this connection. Receive
* thread receives all the messages from this connection and passes them on to
* main thread.
*/
public void startRecSendThreads() {
Connection connection = this;
keepRunning = true;
// start the receiving thread
recThread = new Thread(new Runnable() {
public void run() {
try {
while (connection.keepRunning) {
// Msg format [msgLen(32 bits)][msg(msgLen bytes)]
int len = dataIn.readInt();
byte msg[] = new byte[len];
int off = 0;
while (len > 0) {
int read = dataIn.read(msg, off, len);
if (read < 1)
throw new Exception();
off += read;
len -= read;
}
inQueue.add(new Msg(Msg.INCOMING_MSG, msg, connection));
}
} catch (Exception e) {
// notify dead connection
inQueue.add(new Msg(Msg.CONNECTION_ENDED, null, connection));
}
}
});
recThread.start();
// start the sending thread
sendThread = new Thread(new Runnable() {
@Override
public void run() {
Random rand = new Random();
try {
while (connection.keepRunning) {
// wait for a msg to be placed in outQueue and then take it
Msg msg = outQueue.poll(100, TimeUnit.MILLISECONDS);
if (msg != null) {
dataOut.writeInt(msg.msg.length);
dataOut.write(msg.msg);
dataOut.flush();
}
}
} catch (Exception e) {
// notify dead connection
inQueue.add(new Msg(Msg.CONNECTION_ENDED, e.getMessage().getBytes(), connection));
}
}
});
sendThread.start();
isActive = true;
}
/**
* Closes the connection's socket and stops the send and receive threads.
*/
public void close() {
if (isActive) {
try {
keepRunning = false;
try {
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
recThread.interrupt();
recThread.join();
sendThread.interrupt();
sendThread.join();
isActive = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
/**
*
* This class provides support for handling AIO files.
*
*/
public class FileOps {
/*
* List all the files and folders inside the given directory.
*/
static ArrayList<String> listFiles(File dir, String prefix) {
ArrayList<String> flist = new ArrayList<String>();
if (!dir.exists() || !dir.isDirectory())
return flist;
prefix += dir.getName() + "/";
flist.add(prefix);
for (File file : dir.listFiles()) {
if (file.isDirectory()) {
flist.addAll(listFiles(file, prefix));
} else {
flist.add(prefix + file.getName());
}
}
return flist;
}
/**
* Deletes the given directory.
*
* @param dir : directory to delete
*/
public static void deleteDir(File dir) {
if (!dir.exists() || !dir.isDirectory())
return;
for (File file : dir.listFiles()) {
if (file.isDirectory())
deleteDir(file);
else
file.delete();
}
dir.delete();
}
/**
* Create the whole folder with all the files inside from the given AIO file.
*
* @param aio : AIO file to create the folder from
* @param parentDir : create folder at this location
* @throws IOException
*/
public static void createFolderFromAIOFile(File aio, String parentDir) throws IOException {
DataInputStream din = new DataInputStream(new FileInputStream(aio));
int fileCount = din.readInt();
for (int i = 0; i < fileCount; i++) {
String fname = din.readUTF();
int isDir = din.readInt();
if (isDir == 1) {
new File(parentDir + fname).mkdir();
} else {
DataOutputStream dout = new DataOutputStream(new FileOutputStream(new File(parentDir + fname)));
long size = din.readLong();
byte b[] = new byte[10240];
while (size > 0) {
int read = din.read(b, 0, (int) Math.min(size, b.length));
dout.write(b, 0, read);
size -= read;
}
dout.close();
}
}
din.close();
}
/**
* Creates AIO file from the given folder.
*
* @param srcDir : folder from which to create AIO file
* @param aio : output AIO file
* @throws IOException
*/
public static void createAIOFile(File srcDir, File aio) throws IOException {
ArrayList<String> fileList = listFiles(srcDir, "");
DataOutputStream dout = new DataOutputStream(new FileOutputStream(aio));
dout.writeInt(fileList.size());
for (String s : fileList) {
dout.writeUTF(s);
String fullName = srcDir.getAbsoluteFile().getParent() + "/" + s;
if (new File(fullName).isDirectory()) {
dout.writeInt(1);
} else {
dout.writeInt(0);
File srcFile = new File(fullName);
long size = srcFile.length();
dout.writeLong(size);
DataInputStream din = new DataInputStream(new FileInputStream(srcFile));
byte b[] = new byte[10240];
while (size > 0) {
int read = din.read(b);
dout.write(b, 0, read);
size -= read;
}
din.close();
}
}
dout.close();
}
}
import java.awt.Container;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPasswordField;
import javax.swing.JTextArea;
import javax.swing.JTextField;
/**
*
* GUI for the login form.
*
*/
public class LoginFrame extends JFrame {
LinkedBlockingQueue<Msg> inbox;
JTextArea output = new JTextArea();
JLabel usernameLabel = new JLabel("Username");
JTextField username = new JTextField();
JLabel passwordLabel = new JLabel("Password");
JPasswordField password = new JPasswordField();
JButton login = new JButton("Login");
JButton gotoSignup = new JButton("Go to Sign up");
Container container;
int frameWidth = 500;
int marginTop = 50;
int marginBottom = 50;
int verticalGap = 10;
int outputWidth = 450;
int outputHeight = 200;
int usernameLabelWidth = 300;
int usernameLabelHeight = 40;
int usernameWidth = 300;
int usernameHeight = 40;
int passwordLabelWidth = 300;
int passwordLabelHeight = 40;
int passwordWidth = 300;
int passwordHeight = 40;
int loginWidth = 200;
int loginHeight = 50;
int gotoSignupWidth = 200;
int gotoSignupHeight = 50;
LoginFrame(LinkedBlockingQueue<Msg> inbox, String msg) {
this.inbox = inbox;
container = getContentPane();
container.setLayout(null);
int y = marginTop;
output.setFont(new Font(null, Font.PLAIN, 20));
output.setEditable(false);
output.setText(msg);
output.setLineWrap(true);
output.setMargin(new Insets(10, 10, 10, 10));
output.setBounds((frameWidth - outputWidth) / 2, y, outputWidth, outputHeight);
container.add(output);
y += outputHeight + verticalGap;
usernameLabel.setFont(new Font(null, Font.PLAIN, 18));
usernameLabel.setBounds((frameWidth - usernameLabelWidth) / 2, y, usernameLabelWidth, usernameLabelHeight);
container.add(usernameLabel);
y += usernameLabelHeight + verticalGap;
username.setFont(new Font(null, Font.PLAIN, 18));
username.setBounds((frameWidth - usernameWidth) / 2, y, usernameWidth, usernameHeight);
container.add(username);
y += usernameHeight + verticalGap;
passwordLabel.setFont(new Font(null, Font.PLAIN, 18));
passwordLabel.setBounds((frameWidth - passwordLabelWidth) / 2, y, passwordLabelWidth, passwordLabelHeight);
container.add(passwordLabel);
y += passwordLabelHeight + verticalGap;
password.setFont(new Font(null, Font.PLAIN, 18));
password.setBounds((frameWidth - passwordWidth) / 2, y, passwordWidth, passwordHeight);
container.add(password);
y += passwordHeight + verticalGap;
login.setBounds((frameWidth - loginWidth) / 2, y, loginWidth, loginHeight);
container.add(login);
y += loginHeight + verticalGap;
gotoSignup.setBounds((frameWidth - gotoSignupWidth) / 2, y, gotoSignupWidth, gotoSignupHeight);
container.add(gotoSignup);
y += gotoSignupHeight + verticalGap;
y += marginBottom;
setSize(frameWidth, y);
setTitle("Login");
login.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
Msg m = new Msg(Msg.LOGIN_ALARM, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeUTF(username.getText());
dout.writeUTF(password.getText());
m.prepare();
inbox.add(m);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
gotoSignup.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Msg m = new Msg(Msg.GOTO_SIGNUP, null);
inbox.add(m);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
}
/**
* Update the output text.
*
* @param msg : new output text
*/
public void updateOutput(String msg) {
output.setText(msg);
revalidate();
}
}
\ No newline at end of file
import java.awt.Container;
import java.awt.Font;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.LinkedBlockingQueue;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextArea;
/**
*
* GUI for when client is logged in.
*
*/
public class MainFrame extends JFrame {
LinkedBlockingQueue<Msg> inbox;
String msg1 = "", msg2 = "";
JLabel autoSyncLabel1 = new JLabel();
JLabel autoSyncLabel2 = new JLabel();
JButton autoSyncButton = new JButton();
JLabel userNamelabel = new JLabel();
JTextArea output = new JTextArea();
JButton syncUp = new JButton("Sync Up");
JButton syncDown = new JButton("Sync Down");
Container container;
int frameWidth = 500;
int marginTop = 20;
int marginBottom = 50;
int marginRight = 10;
int verticalGap = 10;
int horizontalGap = 10;
int autoSyncLabel1Width = 200;
int autoSyncLabel1Height = 20;
int autoSyncLabel2Width = 200;
int autoSyncLabel2Height = 20;
int autoSyncButtonWidth = 200;
int autoSyncButtonHeight = 30;
int userNameLabelWidth = 450;
int userNameLabelHeight = 40;
int outputWidth = 450;
int outputHeight = 200;
int syncUpWidth = 200;
int syncUpHeight = 50;
int syncDownWidth = 200;
int syncDownHeight = 50;
MainFrame(LinkedBlockingQueue<Msg> inbox, String m1, String m2, String userNameLabelText) {
this.inbox = inbox;
if (m1 != null)
msg1 = m1;
if (m2 != null)
msg2 = m2;
container = getContentPane();
container.setLayout(null);
int y = marginTop;
autoSyncLabel1.setFont(new Font(null, Font.PLAIN, 15));
autoSyncLabel1.setBounds(frameWidth - marginRight - autoSyncLabel1Width, y, autoSyncLabel1Width,
autoSyncLabel1Height);
container.add(autoSyncLabel1);
y += autoSyncLabel1Height + 3;
autoSyncLabel2.setFont(new Font(null, Font.PLAIN, 15));
autoSyncLabel2.setBounds(frameWidth - marginRight - autoSyncLabel2Width, y, autoSyncLabel2Width,
autoSyncLabel2Height);
container.add(autoSyncLabel2);
y += autoSyncLabel2Height + 3;
autoSyncButton.setBounds(frameWidth - marginRight - autoSyncButtonWidth, y, autoSyncButtonWidth,
autoSyncButtonHeight);
container.add(autoSyncButton);
y += autoSyncButtonHeight + 3;
userNamelabel.setText(userNameLabelText);
userNamelabel.setFont(new Font(null, Font.PLAIN, 15));
userNamelabel.setBounds((frameWidth - userNameLabelWidth) / 2, y, userNameLabelWidth, userNameLabelHeight);
container.add(userNamelabel);
y += userNameLabelHeight + verticalGap;
output.setFont(new Font(null, Font.PLAIN, 20));
output.setEditable(false);
output.setText(msg1 + "\n" + msg2);
output.setLineWrap(true);
output.setMargin(new Insets(10, 10, 10, 10));
output.setBounds((frameWidth - outputWidth) / 2, y, outputWidth, outputHeight);
container.add(output);
y += outputHeight + verticalGap;
int x = (frameWidth - (syncUpWidth + horizontalGap + syncDownWidth)) / 2;
syncUp.setBounds(x, y, syncUpWidth, syncUpHeight);
container.add(syncUp);
x += syncUpWidth + horizontalGap;
syncDown.setBounds(x, y, syncDownWidth, syncDownHeight);
container.add(syncDown);
y += syncDownHeight + verticalGap;
y += marginBottom;
setSize(frameWidth, y);
setTitle("FSync");
syncUp.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Msg m = new Msg(Msg.SYNC_UP_ALARM, null);
inbox.add(m);
}
});
syncDown.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Msg m = new Msg(Msg.SYNC_DOWN_ALARM, null);
inbox.add(m);
}
});
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setResizable(false);
}
/**
* Update the output text.
*
* @param m1 : line1 text, if null then line1 text is unchanged.
* @param m2 : line2 text, if null then line2 text is unchanged.
*/
public synchronized void updateOutput(String m1, String m2) {
if (m1 != null)
msg1 = m1;
if (m2 != null)
msg2 = m2;
output.setText(msg1 + "\n" + msg2);
revalidate();
}
/**
* Update the auto sync status.
*
* @param isOn : is auto sync on.
* @param interval : auto sync time interval
*/
public synchronized void updateAutoSyncStatus(boolean isOn, int interval) {
if (isOn) {
autoSyncLabel1.setText("Auto Sync : On");
autoSyncLabel2.setText("Every " + interval + " seconds");
autoSyncButton.setText("Turn Off");
for (ActionListener al : autoSyncButton.getActionListeners()) {
autoSyncButton.removeActionListener(al);
}
autoSyncButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Msg m = new Msg(Msg.TURN_OFF_AUTO_SYNC, null);
inbox.add(m);
}
});
} else {
autoSyncLabel1.setText("Auto Sync : Off");
autoSyncLabel2.setText("");
autoSyncButton.setText("Turn On");
for (ActionListener al : autoSyncButton.getActionListeners()) {
autoSyncButton.removeActionListener(al);
}
autoSyncButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
SetIntervalFrame sif = new SetIntervalFrame(inbox);
sif.setLocation(getX(), getY() + (getHeight() - sif.getHeight()) / 2);
sif.setVisible(true);
}
});
}
revalidate();
}
}
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
/**
*
* Represents a message sent either over the network or internally. Defines all
* the message types and message names.
*