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.
*
*/
public class Msg {
final static int CONNECTION_ENDED = 1;
final static int INCOMING_MSG = 2;
final static int OUTGOING_MSG = 3;
final static int ALARM = 4;
final static int SYNC_THREAD_ENDED = 5;
final static int END_SYNC_ALARM = 6;
final static int SIGNUP = 100;
final static int LOGIN = 101;
final static int SIGNUP_RESULT = 200;
final static int LOGIN_RESULT = 201;
final static int TURN_ON_AUTO_SYNC = 301;
final static int TURN_OFF_AUTO_SYNC = 302;
final static int LOGIN_ERROR = 1000;
final static int LOGIN_SUCCSESS = 1001;
final static int SIGNUP_ERROR = 1002;
final static int SIGNUP_SUCCSESS = 1003;
final static int SIGNUP_ALARM = 2001;
final static int LOGIN_ALARM = 2002;
final static int SYNC_UP_ALARM = 2003;
final static int SYNC_DOWN_ALARM = 2004;
final static int GOTO_LOGIN = 2005;
final static int GOTO_SIGNUP = 2006;
final static int INIT_SYNC_UP_REQ_FROM_CLIENT = 3001;
final static int INIT_SYNC_DOWN_REQ_FROM_CLIENT = 3002;
final static int INIT_SYNC_DOWN_INSTRUCTION_FROM_SERVER = 3003;
final static int SYNC_UP_REQ_DENIED = 4001;
final static int SYNC_DOWN_REQ_DENIED = 4002;
final static int SYNC_DOWN_REQ_GO_AHEAD = 4003;
final static int SIGFILE = 5001;
final static int DELTAFILE = 5002;
int type;
byte msg[];
Connection connection;
Object userData; // can be used to store arbitrary data
private DataInputStream din = null;
private DataOutputStream dout = null;
private ByteArrayOutputStream bout = null;
/**
* @param type : Message type
* @param msg : Message content
* @param connection : network connection associated with the message
*/
public Msg(int type, byte msg[], Connection connection) {
this.type = type;
this.msg = msg;
this.connection = connection;
}
/**
* @param type : Message type
* @param userData : Any arbitrary data to be associated with message
*/
public Msg(int type, Object userData) {
this.type = type;
this.userData = userData;
}
/**
* Resets the input stream of the message
*/
public void resetDataInputStream() {
din = null;
}
/**
* Get the input stream to read the message content.
*
* @return Input stream of content of the message
*/
public DataInputStream getDataInputStream() {
if (din == null) {
din = new DataInputStream(new ByteArrayInputStream(msg));
}
return din;
}
/**
* Get the output stream to write the message content.
*
* @return Output stream for content of the message
*/
public DataOutputStream getDataOutputStream() {
if (dout == null) {
bout = new ByteArrayOutputStream();
dout = new DataOutputStream(bout);
}
return dout;
}
/**
* Call this method when done writing to the output stream of the message to
* prepare the message to be sent out.
*
* @throws IOException
*/
public void prepare() throws IOException {
dout.flush();
bout.flush();
msg = bout.toByteArray();
}
}
/**
*
* Contains the rolling hash of a sequence of bytes.
*
*/
public class RollingHash {
private final int X = 31;
private int XtoN;
private int hash;
/**
* @param blockSize : size of byte sequence
*/
public RollingHash(int blockSize) {
XtoN = 1;
while (blockSize-- > 0)
XtoN *= X;
}
/**
* @return : current value of hash
*/
public int getHash() {
return hash;
}
/**
* Resets the hash value to 0.
*/
public void reset() {
hash = 0;
}
/**
* Add the given byte to the sequence and update the hash.
*
* @param b : byte to be added
*/
public void update(byte b) {
hash = X * hash + b;
}
/**
* Add one byte and delete another from the sequence and update the hash.
*
* @param inByte : byte to be added
* @param outByte : byte to be deleted
*/
public void update(byte inByte, byte outByte) {
hash = X * hash + inByte - XtoN * outByte;
}
/**
* Add len number of bytes from arr starting from offset to the sequence and
* update the hash.
*
* @param arr : array containing bytes
* @param offset : starting position
* @param len : number of bytes to add
*/
public void update(byte[] arr, int offset, int len) {
for (int i = offset; i < offset + len; i++) {
hash = X * hash + arr[i];
}
}
}
import java.awt.Container;
import java.awt.Font;
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.JTextField;
/**
*
* GUI for setting auto sync interval
*
*/
public class SetIntervalFrame extends JFrame {
static int minInterval = 10;
static int maxInterval = 1000000;
LinkedBlockingQueue<Msg> inbox;
JLabel label = new JLabel("Set auto sync interval in seconds (" + minInterval + " - " + maxInterval + ")");
JTextField interval = new JTextField();
JButton ok = new JButton("OK");
Container container;
int frameWidth = 500;
int marginTop = 10;
int marginBottom = 30;
int verticalGap = 10;
int labelWidth = 450;
int labelHeight = 40;
int intervalWidth = 350;
int intervalHeight = 40;
int okWidth = 100;
int okHeight = 30;
public SetIntervalFrame(LinkedBlockingQueue<Msg> inbox) {
this.inbox = inbox;
container = getContentPane();
container.setLayout(null);
int y = marginTop;
label.setFont(new Font(null, Font.PLAIN, 15));
label.setBounds((frameWidth - labelWidth) / 2, y, labelWidth, labelHeight);
container.add(label);
y += labelHeight + verticalGap;
interval.setFont(new Font(null, Font.PLAIN, 15));
interval.setBounds((frameWidth - intervalWidth) / 2, y, intervalWidth, intervalHeight);
container.add(interval);
y += intervalHeight + verticalGap;
ok.setBounds((frameWidth - okWidth) / 2, y, okWidth, okHeight);
container.add(ok);
y += okHeight + verticalGap;
y += marginBottom;
setSize(frameWidth, y);
setTitle("Set Interval");
ok.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
int v = Integer.parseInt(interval.getText());
if (v >= minInterval && v <= maxInterval) {
Msg m = new Msg(Msg.TURN_ON_AUTO_SYNC, Integer.valueOf(v));
inbox.add(m);
setVisible(false);
}
} catch (NumberFormatException exc) {
}
}
});
setResizable(false);
}
}
\ 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.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 signup form.
*
*/
public class SignupFrame 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 signup = new JButton("Signup");
JButton gotoLogin = new JButton("Go to Login");
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 signupWidth = 200;
int signupHeight = 50;
int gotoLoginWidth = 200;
int gotoLoginHeight = 50;
SignupFrame(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;
signup.setBounds((frameWidth - signupWidth) / 2, y, signupWidth, signupHeight);
container.add(signup);
y += signupHeight + verticalGap;
gotoLogin.setBounds((frameWidth - gotoLoginWidth) / 2, y, gotoLoginWidth, gotoLoginHeight);
container.add(gotoLogin);
y += gotoLoginHeight + verticalGap;
y += marginBottom;
setSize(frameWidth, y);
setTitle("Signup");
signup.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
Msg m = new Msg(Msg.SIGNUP_ALARM, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeUTF(username.getText());
dout.writeUTF(password.getText());
m.prepare();
inbox.add(m);
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
gotoLogin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Msg m = new Msg(Msg.GOTO_LOGIN, 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
This diff is collapsed.
#!/bin/bash
javac *.java
jar cf Client.jar *.class
rm *.class
echo '#!/bin/bash' > RunClient.sh
echo 'java -classpath ".:Client.jar" Client $1' >> RunClient.sh
chmod 777 RunClient.sh
echo "Done"
SERVER_ADDR:192.168.43.144
SERVER_PORT:12345
OPERATION_DIR:/path/to/directory/
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
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.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
/**
*
* Represents a sqlite database used to store client login information.
*
*/
public class Database {
String dbFile;
java.sql.Connection dbConnection;
/**
*
* @param dbFile : Database file to be used. If the file does not exist it will
* be created.
*/
public Database(String dbFile) {
this.dbFile = dbFile;
try {
Class.forName("org.sqlite.JDBC");
dbConnection = DriverManager.getConnection("jdbc:sqlite:" + dbFile);
Statement stmt = dbConnection.createStatement();
String sql;
try {
sql = "CREATE TABLE USERS (uname TEXT PRIMARY KEY NOT NULL, pass TEXT NOT NULL)";
stmt.executeUpdate(sql);
} catch (SQLException e) {
}
stmt.close();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Add a new user to the database.
*
* @param uname : username
* @param pass : password
* @return : true if user was added false otherwise
*/
public boolean addUser(String uname, String pass) {
PreparedStatement stmt;
try {
stmt = dbConnection.prepareStatement("INSERT INTO USERS (uname,pass) " + "VALUES (?,?);");
stmt.setString(1, uname);
stmt.setString(2, pass);
stmt.executeUpdate();
stmt.close();
return true;
} catch (SQLException e) {
}
return false;
}
/**
* Check if the given username and password is valid.
*
* @param uname : username
* @param pass : password
* @return : true if login is valid false otherwise
*/
public boolean checkLogin(String uname, String pass) {
try {
PreparedStatement stmt;
stmt = dbConnection.prepareStatement("SELECT * from USERS where uname = ? AND pass = ?;");
stmt.setString(1, uname);
stmt.setString(2, pass);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return true;
}
} catch (SQLException e) {
}
return false;
}
}
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.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
* Represents the server socket used to accept client connections.
*
*/
public class ListeningSocket {
int port;
LinkedBlockingQueue<Msg> inbox;
ServerSocket serverSocket = null;
/**
* @param inbox : inbox of main thread
*/
public ListeningSocket(LinkedBlockingQueue<Msg> inbox) {
this.inbox = inbox;
port = 0;
}
/**
*
* @param port : port number on which to listen for new connections
* @param inbox : inbox of main thread
*/
public ListeningSocket(int port, LinkedBlockingQueue<Msg> inbox) {
this.inbox = inbox;
this.port = port;
}
/**
* Creates a server socket and starts a new thread to accept the incoming
* connections.
*
* @throws IOException
*/
public void start() throws IOException {
serverSocket = new ServerSocket(port);
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Socket socket = serverSocket.accept();
Connection connection = new Connection(socket, inbox);
connection.startRecSendThreads();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
*
* @return port number on which socket is running or 0 if the socket is not
* running
*/
public int getPort() {
if (serverSocket != null)
return serverSocket.getLocalPort();
return 0;
}
/**
* @return local address of the socket
*/
public String getSocketAddr() {
if (serverSocket != null)
return serverSocket.getLocalSocketAddress().toString();
return null;
}
}
\ No newline at end of file
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingQueue;
/**
*
* Represents the main thread server thread. It receives all the messages from
* clients and forwards them to appropriate SyncThreads.
*
*/
public class MainThread {
int port;
LinkedBlockingQueue<Msg> inbox;
ListeningSocket listeningSocket;
String dbFileName = "SyncServer.db";
Database database;
Map<String, SyncThread> syncThreadMap;
static String mainFolderName = "FSync";
static String tempFolderName = ".temp";
String parentDir;
String configFileName;
/**
*
* @param cfn : config file name
*/
public MainThread(String cfn) {
configFileName = cfn;
inbox = new LinkedBlockingQueue<Msg>();
syncThreadMap = new HashMap<String, SyncThread>();
}
/**
* Reads the config file and sets port and working directory.
*
* @throws IOException
*/
public void readConfig() throws IOException {
BufferedReader in = new BufferedReader(new FileReader(new File(configFileName)));
while (true) {
String line = in.readLine();
if (line == null)
break;
if (line.length() < 1 || line.startsWith("#"))
continue;
String tokens[] = line.split(":");
if (tokens[0].equals("PORT")) {
port = Integer.parseInt(tokens[1]);
} else if (tokens[0].equals("OPERATION_DIR")) {
parentDir = tokens[1];
}
}
in.close();
}
/**
* Processes login request from client and replies accordingly.
*
* @param msg : contains login request
* @return : username if valid login else null
* @throws IOException
*/
public String checkLogin(Msg msg) throws IOException {
DataInputStream din = msg.getDataInputStream();
String uname = din.readUTF();
String pass = din.readUTF();
if (!database.checkLogin(uname, pass)) {
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(Msg.LOGIN_RESULT);
dout.writeInt(Msg.LOGIN_ERROR);
dout.writeUTF("Wrong username or password!");
m.prepare();
msg.connection.send(m);
return null;
} else {
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(Msg.LOGIN_RESULT);
dout.writeInt(Msg.LOGIN_SUCCSESS);
dout.writeUTF(uname);
m.prepare();
msg.connection.send(m);
return uname;
}
}
/**
* Processes signup request from client and replies accordingly.
*
* @param msg : contains signup request
* @return : username if signup successful else null
* @throws IOException
*/
public String signup(Msg msg) throws IOException {
DataInputStream din = msg.getDataInputStream();
String uname = din.readUTF();
String pass = din.readUTF();
if (uname.length() < 1 || pass.length() < 1) {
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(Msg.SIGNUP_RESULT);
dout.writeInt(Msg.SIGNUP_ERROR);
dout.writeUTF("Username and password must not be empty!");
m.prepare();
msg.connection.send(m);
return null;
} else {
if (!database.addUser(uname, pass)) {
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(Msg.SIGNUP_RESULT);
dout.writeInt(Msg.SIGNUP_ERROR);
dout.writeUTF("Username taken!");
m.prepare();
msg.connection.send(m);
return null;
} else {
Msg m = new Msg(Msg.OUTGOING_MSG, null);
DataOutputStream dout = m.getDataOutputStream();
dout.writeInt(Msg.SIGNUP_RESULT);
dout.writeInt(Msg.SIGNUP_SUCCSESS);
m.prepare();
msg.connection.send(m);
return uname;
}
}
}
/**
* Calls checkLogin and adds the client to appropriate sync thread if valid
* login. If sync thread for the client is not running start a new sync thread
* for the client.
*
* @param msg : contains login request
* @throws IOException
*/
public void handleLogin(Msg msg) throws IOException {
System.out.println("New login req");
String uname = checkLogin(msg);
if (uname != null) {
System.out.println(uname + " logged in");
msg.connection.userName = uname;
SyncThread syncThread = syncThreadMap.get(uname);
if (syncThread == null) {
syncThread = new SyncThread(inbox, parentDir, uname);
syncThreadMap.put(uname, syncThread);
syncThread.keepRunning = true;
syncThread.start();
}
syncThread.lock.lock();
syncThread.clients.add(msg.connection);
syncThread.lock.unlock();
}
}
/**
* Calls signup and creates appropriate directories for the new user if signup
* is successful.
*
* @param msg : contains signup request
* @throws IOException
*/
public void handleSignup(Msg msg) throws IOException {
System.out.println("new signup req");
String uname = signup(msg);
if (uname != null) {
System.out.println(uname + " signed up");
File f;
f = new File(parentDir + "userdata/" + uname + "/" + mainFolderName);
f.mkdirs();
f = new File(parentDir + "userdata/" + uname + "/" + tempFolderName);
f.mkdirs();
}
}
/**
* Reads config file, initializes database, starts listening socket and waits
* for the incoming messages.
*
* @throws IOException
*/
public void start() throws IOException {
readConfig();
database = new Database(parentDir + dbFileName);
listeningSocket = new ListeningSocket(port, inbox);
listeningSocket.start();
System.out.println("running on port : " + port);
while (true) {
Msg msg;
try {
msg = inbox.take();
if (msg.type == Msg.CONNECTION_ENDED) {
if (msg.connection.userName != null) {
SyncThread syncThread = syncThreadMap.get(msg.connection.userName);
syncThread.inbox.add(msg);
}
} else if (msg.type == Msg.SYNC_THREAD_ENDED) {
SyncThread syncThread = syncThreadMap.get((String) msg.userData);
syncThread.t.join();
System.out.println("Joined sync thread, user : " + (String) msg.userData);
syncThreadMap.remove((String) msg.userData);
} else if (msg.type == Msg.INCOMING_MSG) {
DataInputStream din = msg.getDataInputStream();
int msgName = din.readInt();
Connection conn = msg.connection;
if (conn.userName == null) {
if (msgName == Msg.LOGIN) {
handleLogin(msg);
} else if (msgName == Msg.SIGNUP) {
handleSignup(msg);
}
} else {
msg.resetDataInputStream();
SyncThread syncThread = syncThreadMap.get(conn.userName);
syncThread.inbox.add(msg);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
\ No newline at end of file
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.
*
*/
public class Msg {
final static int CONNECTION_ENDED = 1;
final static int INCOMING_MSG = 2;
final static int OUTGOING_MSG = 3;
final static int ALARM = 4;
final static int SYNC_THREAD_ENDED = 5;
final static int END_SYNC_ALARM = 6;
final static int SIGNUP = 100;
final static int LOGIN = 101;
final static int SIGNUP_RESULT = 200;
final static int LOGIN_RESULT = 201;
final static int TURN_ON_AUTO_SYNC = 301;
final static int TURN_OFF_AUTO_SYNC = 302;
final static int LOGIN_ERROR = 1000;
final static int LOGIN_SUCCSESS = 1001;
final static int SIGNUP_ERROR = 1002;
final static int SIGNUP_SUCCSESS = 1003;
final static int SIGNUP_ALARM = 2001;
final static int LOGIN_ALARM = 2002;
final static int SYNC_UP_ALARM = 2003;
final static int SYNC_DOWN_ALARM = 2004;
final static int GOTO_LOGIN = 2005;
final static int GOTO_SIGNUP = 2006;
final static int INIT_SYNC_UP_REQ_FROM_CLIENT = 3001;
final static int INIT_SYNC_DOWN_REQ_FROM_CLIENT = 3002;
final static int INIT_SYNC_DOWN_INSTRUCTION_FROM_SERVER = 3003;
final static int SYNC_UP_REQ_DENIED = 4001;
final static int SYNC_DOWN_REQ_DENIED = 4002;
final static int SYNC_DOWN_REQ_GO_AHEAD = 4003;
final static int SIGFILE = 5001;
final static int DELTAFILE = 5002;
int type;
byte msg[];
Connection connection;
Object userData; // can be used to store arbitrary data
private DataInputStream din = null;
private DataOutputStream dout = null;
private ByteArrayOutputStream bout = null;
/**
* @param type : Message type
* @param msg : Message content
* @param connection : network connection associated with the message
*/
public Msg(int type, byte msg[], Connection connection) {
this.type = type;
this.msg = msg;
this.connection = connection;
}
/**
* @param type : Message type
* @param userData : Any arbitrary data to be associated with message
*/
public Msg(int type, Object userData) {
this.type = type;
this.userData = userData;
}
/**
* Resets the input stream of the message
*/
public void resetDataInputStream() {
din = null;
}
/**
* Get the input stream to read the message content.
*
* @return Input stream of content of the message
*/
public DataInputStream getDataInputStream() {
if (din == null) {
din = new DataInputStream(new ByteArrayInputStream(msg));
}
return din;
}
/**
* Get the output stream to write the message content.
*
* @return Output stream for content of the message
*/
public DataOutputStream getDataOutputStream() {
if (dout == null) {
bout = new ByteArrayOutputStream();
dout = new DataOutputStream(bout);
}
return dout;
}
/**
* Call this method when done writing to the output stream of the message to
* prepare the message to be sent out.
*
* @throws IOException
*/
public void prepare() throws IOException {
dout.flush();
bout.flush();
msg = bout.toByteArray();
}
}
/**
*
* Contains the rolling hash of a sequence of bytes.
*
*/
public class RollingHash {
private final int X = 31;
private int XtoN;
private int hash;
/**
* @param blockSize : size of byte sequence
*/
public RollingHash(int blockSize) {
XtoN = 1;
while (blockSize-- > 0)
XtoN *= X;
}
/**
* @return : current value of hash
*/
public int getHash() {
return hash;
}
/**
* Resets the hash value to 0.
*/
public void reset() {
hash = 0;
}
/**
* Add the given byte to the sequence and update the hash.
*
* @param b : byte to be added
*/
public void update(byte b) {
hash = X * hash + b;
}
/**
* Add one byte and delete another from the sequence and update the hash.
*
* @param inByte : byte to be added
* @param outByte : byte to be deleted
*/
public void update(byte inByte, byte outByte) {
hash = X * hash + inByte - XtoN * outByte;
}
/**
* Add len number of bytes from arr starting from offset to the sequence and
* update the hash.
*
* @param arr : array containing bytes
* @param offset : starting position
* @param len : number of bytes to add
*/
public void update(byte[] arr, int offset, int len) {
for (int i = offset; i < offset + len; i++) {
hash = X * hash + arr[i];
}
}
}
import java.io.IOException;
/**
*
* Contains the main method which creates and starts MainThread of the server.
*
*/
public class Server {
public static void main(String[] args) throws IOException {
if (args.length < 1) {
System.out.println("No config file provided! Exiting.");
System.exit(0);
}
MainThread mainThread = new MainThread(args[0]);
mainThread.start();
}
}
This diff is collapsed.
This diff is collapsed.
#!/bin/bash
javac *.java
jar cf Server.jar *.class
rm *.class
echo '#!/bin/bash' > RunServer.sh
echo 'java -classpath ".:sqlite-jdbc-3.7.2.jar:Server.jar" Server $1' >> RunServer.sh
chmod 777 RunServer.sh
echo "Done"
PORT:12345
OPERATION_DIR:/path/to/directory/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment