public synchronized void setRunning(boolean newValue) {
running = newValue;
}
public void killThread() {
setRunning(false);
}
public void run()
{
setRunning(true);
boolean shutdownNow = false;
indexLines = new Vector<String>();
index_lines = new Vector<String[]>();
//Set up the network socket.
try {
downloadSocket = new Socket(SERVER_ADDRESS,DOWNLOAD_PORT);
downloadSocket.setSoTimeout(SOCKET_TIMEOUT);
} catch(Exception e) { System.err.println(
"**Error setting up client download socket! Client failure.");
shutdownNow = true;
}
if(shutdownNow) {
try {
downloadSocket.close();
} catch(Exception e) {
System.err.println(
"Error shutting down client download socket after socket setup failure!");
}
setRunning(false);
return;
}
//Acquire index from the server.
indexLines = acquireIndexOrHashes(true,"");
if(!running) return;
//Build the index as a string array vector.
try {
buildStringIndex();
} catch(Exception e) {
System.err.println("Error building index string array!");
}
Vector<String> missing_datafiles = new Vector<String>();
//Add the remote datafiles that are missing entirely to local data.
int num_new_lines;
for(int count=0;count<missing_datafiles.size();count++) {
acquireDataFile(missing_datafiles.elementAt(count));
if(!running) return;
}
Vector<String> hashesToUpload = new Vector<String>();
Vector<String> remoteHashes = new Vector<String>();
Vector<String> localHashes = new Vector<String>();
Vector<String> entriesToDownload = new Vector<String>();
//Set up the localHashes vector.
localHashes = data_manager.getHashList(currentDataFile);
//This is the main method for determining differences.
//It is very important.
setupDownloadsAndUploads(localHashes, remoteHashes,
entriesToDownload,
hashesToUpload);
//Pull all of the necessary downloads.
requestDownloads(currentDataFile, entriesToDownload, false);
try {
downloadSocket.close();
} catch(Exception e) {
System.err.println(
"Error shutting down client download socket at end of cycle!");
}
if(!running()) {
System.err.println("Error requesting downloads. Exiting.");
return;
}
//Upload the necessary uploads.
uploadData(currentDataFile, hashesToUpload);
try {
uploadSocket.close();
} catch(Exception e) {
System.err.println(
"Error shutting down client upload socket at end of cycle!");
}
if(!running()) {
System.err.println("Error uploading all hashes. Exiting.");
return;
}
}
setRunning(false);
return;
}
//This method can request an entire file (in which case the hashes
//are ignored) or it can request hashes and hash spans.
private void requestDownloads(String currentDataFile,
Vector<String> downloadHashes,
boolean entireFile) {
//If there is nothing to do.
if((downloadHashes.size()==0)&&!entireFile) return;
//Input and Output strings for communications.
String inputString, outputString;
TrueStringTokenizer sttok;
//This tries to get either the first entry or the entire file as
//a ZIP. If we were seeking the entire file and it succeeds, it is
//done. If we were just seeking certain entries, it continues to try
//to get those entries as ZIP, skipping the first one.
//Failure to get ZIP causes a retry for TXT, whether for the entire
//file or for specific entries, starting at the first one.
try {
//Set up the input and output streams.
d_out = new BufferedWriter(new PrintWriter(
downloadSocket.getOutputStream(), true));
d_in = downloadSocket.getInputStream();
byte[] readBytes = new byte[10000];
int bufferContentSize = 0;
String entryLine = new String("");
BufferedInputStream bis = new BufferedInputStream(d_in);
ZipInputStream zis = new ZipInputStream(bis);
boolean zipcapable = false;
//If entireFile, get the entire file.
if(entireFile) outputString = "GET /"+currentDataFile+".zip\n";
//Or get the first hash member, to test for ZIP capabilities.
else outputString = "GET /"+currentDataFile+".zip/"+
downloadHashes.elementAt(0)+"\n";
//Reading with a buffer is more efficient than reading byte
//by byte, but knowing where the data ends becomes an issue.
//Thus the timeout is relied upon. The exception would
//result in a lost data count, thus the precautions.
//This section is for reading ZIP data.
while(!breakWhile) {
try {
//Clear readBytes.
System.arraycopy(blankbytes, 0, readBytes, 0, 10000);
//Fill readBytes.
bufferContentSize = zis.read(readBytes,0,10000);
} catch(Exception e) {
//Likely a port timeout.
//No more reading.
breakWhile = true;
//If there is a port failure, close connection after work.
shutdownNow = true;
//Examine the buffer to see how many bytes are there.
//The zis.read, with exception, will not tell us.
bufferContentSize = 0;
for(int count=0;count<10000;count++) {
if(readBytes[count]==127) {
bufferContentSize = count;
count = 10000;
}
}
}
//If the buffer is empty at this point, ensure that there
//will be no more reads.
if(bufferContentSize<=0) breakWhile = true;
//The buffer has data. Add it to entryLine.
else {
entryLine = entryLine + new String(
readBytes,0,bufferContentSize);
//If EOF, remove the marker from entryLine and quit reading.
if(entryLine.contains("%EOF%")) {
entryLine = entryLine.substring(
0,entryLine.indexOf("%EOF%"));
breakWhile = true;
}
//If the buffer has data, ZIP reading is OK.
zipcapable = true;
System.err.println("ZIP download accepted: "+currentDataFile);
sttok = new TrueStringTokenizer(entryLine,"%LBK%");
//The remainder is recycled in case we are reading
//data longer than the buffer and the looping is necessary.
//Do not use, individually, the last bit after tokens.
while( sttok.hasMoreTokens()
&& sttok.getRemainder().contains("%LBK%") ) {
entryLine = sttok.nextToken();
if(!entryLine.equals("")) {
data_manager.takeDataLine(entryLine+"%LBK%");
}
}
entryLine = sttok.getRemainder();
}
}
//If it is zipcapable, do the other entries in the hashes.
//This does nothing if in entireFile mode.
if(zipcapable&&!shutdownNow) {
//Entries counter is one because of zip testing.
//This is skipped for entireFile.
for(int entriescounter=1;
(entriescounter<downloadHashes.size())&&!entireFile;
entriescounter++)
{
entryLine = "";
outputString = "GET /"+currentDataFile+".zip/"+
downloadHashes.elementAt(entriescounter)+"\n";
//The zip mode has been verified, so bad zip data
//may as well result in disposing of the connection
//by going to the main catch.
zis.getNextEntry();
if(bufferContentSize>=0) {
entryLine = entryLine + new String(
readBytes,0,bufferContentSize);
if(entryLine.contains("%EOF%")) {
entryLine = entryLine.substring(
0,entryLine.indexOf("%EOF%"));
breakWhile = true;
}
sttok = new TrueStringTokenizer(entryLine,
"%LBK%");
while(sttok.hasMoreTokens()
&& sttok.getRemainder().contains("%LBK%")) {
entryLine = sttok.nextToken();
if(!entryLine.equals("")) {
data_manager.takeDataLine(entryLine+
"%LBK%");
}
}
entryLine = sttok.getRemainder();
}
}
}
}
} catch(Exception e) {
System.err.println(
"Client error processing "+currentDataFile+
"! Client failure.");
shutdownNow = true;
}
if(shutdownNow) {
//If there is a major error found when working on data, shut
//down the connection and thread.
try {
downloadSocket.close();
} catch(Exception e) {
System.err.println(
"Error closing client download socket after "+currentDataFile+
" failure!");
}
setRunning(false);
}
}
//To upload local entries identified by uploadHashes
private void uploadData(String dataFileName, Vector<String> uploadHashes) {
if(uploadHashes.size()==0) return;
boolean zipcapable = true;
//ZIPs go to every other port. These ports should be tried first.
//For this implementation, only one ZIP port will be tried.
int uploadingPort = 0;
boolean zipsuccess = true;
String outputString = new String("");
boolean shutdownNow = false;
//Try a ZIP port.
if(UPLOAD_PORTS.length>1) {
uploadingPort = UPLOAD_PORTS[1];
//Set up the network socket.
try {
uploadSocket = new Socket(SERVER_ADDRESS,uploadingPort);
uploadSocket.setSoTimeout(SOCKET_TIMEOUT);
ZipOutputStream zos = new ZipOutputStream(
new BufferedOutputStream(uploadSocket.getOutputStream()));
zos.putNextEntry(new ZipEntry(dataFileName+".txt"));
//Populate the last two vectors.
//This method is the most critical.
private void setupDownloadsAndUploads(Vector<String> localHashes,
Vector<String> remoteHashes, Vector<String> entriesToDownload,
Vector<String> hashesToUpload) {
String spanFirst = new String("");
String spanSecond = new String("");
String current = new String("");
//Set up downloads.
for(int count=0;count<remoteHashes.size();count++) {
//Go through remoteHashes, making a span list.
//Go through the span list, to remove the single entries.
current = remoteHashes.elementAt(count);
if(!localHashes.contains(current)) {
if(spanFirst.equals(""))
spanFirst = current;
else
spanSecond = current;
} else {
//current is already on hand locally. Add current span, if it
//has anything, and clear it.
if((!spanFirst.equals("")) && (!spanSecond.equals(""))) {
entriesToDownload.addElement(spanFirst+"-"+spanSecond);
spanFirst = "";
spanSecond = "";
} else if((!spanFirst.equals(""))) {
//If only the second one is empty...
entriesToDownload.addElement(spanFirst);
spanFirst = "";
}
}
}
if((!spanFirst.equals("")) && (!spanSecond.equals("")))
entriesToDownload.addElement(spanFirst+"-"+spanSecond);
else if((!spanFirst.equals("")))
entriesToDownload.addElement(spanFirst);
//Set up necessary uploads.
for(int count=0;count<localHashes.size();count++) {
current = localHashes.elementAt(count);
if(!remoteHashes.contains(current))
hashesToUpload.addElement(current);
}
}
//Strip index entries that are locally up to date as well as those
//TRANSFER entries that are not locally existent to missing_datafiles.
//The transferred entries are removed from the index so they are not
//reprocessed in search of differences.
private void finalizeIndexForUse(Vector<String[]> index_lines,
Vector<String> missing_datafiles) {
int datafilestatus;
for(int count=0;count<index_lines.size();count++) {
datafilestatus = 0;
datafilestatus = data_manager.hasExactDataFile(
index_lines.elementAt(count)[0],
index_lines.elementAt(count)[1],
index_lines.elementAt(count)[3]);
if(datafilestatus==1) {
index_lines.removeElementAt(count);
count--;
} else if(datafilestatus==0) {
missing_datafiles.addElement(new String(
index_lines.elementAt(count)[0]+
"-"+index_lines.elementAt(count)[1]));
index_lines.removeElementAt(count);
count--;
}
}
}
while(!breakWhile) {
try {
System.arraycopy(blankbytes, 0, readBytes, 0, 10000);
bufferContentSize = zis.read(readBytes,0,10000);
} catch(Exception e) {
//Likely a port timeout.
breakWhile = true;
//Port timeout means to dispose of connection.
shutdownNow = true;
bufferContentSize = 0;
for(int count=0;count<10000;count++) {
if(readBytes[count]==127) {
bufferContentSize = count;
count = 10000;
}
}
}
if(bufferContentSize<=0) breakWhile = true;
else {
System.err.println("ZIP index or hashes download accepted.");
zipcapable = true;
entryLine = entryLine + new String(
readBytes,0,bufferContentSize);
if(entryLine.contains("%EOF%")) {
entryLine = entryLine.substring(
0,entryLine.indexOf("%EOF%"));
breakWhile = true;
}
sttok = new TrueStringTokenizer(entryLine,"%LBK%");
//The remainder is recycled in case we are reading
//data longer than the buffer and the looping is necessary.
//Do not use, individually, the last bit after tokens.
while( sttok.hasMoreTokens()
&& sttok.getRemainder().contains("%LBK%") ) {
entryLine = sttok.nextToken();
if(!entryLine.equals("")) {
results.addElement(entryLine);
}
}
entryLine = sttok.getRemainder();
}
}
//The zip reading while statement has ceased.
if(!zipcapable&&!shutdownNow) {
System.err.println("ZIP Index or Hashes Download Rejected. Going TXT.");
System.err.println("TXT index or hashes download accepted.");
sttok = new TrueStringTokenizer(entryLine,"%LBK%");
//The remainder is recycled in case we are reading
//data longer than the buffer and the looping is
//necessary.
//Do not use, individually, the last bit after tokens.
while( sttok.hasMoreTokens()
&& sttok.getRemainder().contains("%LBK%") ) {
entryLine = sttok.nextToken();
if(!entryLine.equals("")) {
results.addElement(entryLine);
}
}
entryLine = sttok.getRemainder();
}
}
}
} catch(Exception e) {
System.err.println(
"Client error processing index or hashes! Client failure.");
shutdownNow = true;
}
if(shutdownNow) {
System.err.println("Shutting down client socket due to completion or error.");
try {
downloadSocket.close();
} catch(Exception e) {
System.err.println(
"Error closing client download socket after index "+
"or hashes processing failure!");
}
setRunning(false);
return results;
}
return results;
}
//The datafile name should not have the extension.
private void acquireDataFile(String dataFileName) {
requestDownloads(dataFileName, new Vector<String>(), true);
}