From 1ea680f262cfb3488917a8e14b170f99471552fb Mon Sep 17 00:00:00 2001 From: Peter Nerg Date: Sun, 20 May 2012 19:03:04 +0200 Subject: [PATCH] Patch for JGRP-1448 --- src/org/jgroups/protocols/FILE_PING.java | 220 +++++++++++++++++++++--------- 1 file changed, 158 insertions(+), 62 deletions(-) diff --git a/src/org/jgroups/protocols/FILE_PING.java b/src/org/jgroups/protocols/FILE_PING.java index 86300c4..9126463 100644 --- a/src/org/jgroups/protocols/FILE_PING.java +++ b/src/org/jgroups/protocols/FILE_PING.java @@ -9,6 +9,7 @@ import org.jgroups.util.UUID; import org.jgroups.util.Util; import java.io.*; +import java.nio.channels.FileChannel; import java.util.*; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; @@ -145,82 +146,122 @@ public class FILE_PING extends Discovery { if(!dir.exists()) return; - try { - String filename=addr instanceof UUID? ((UUID)addr).toStringLong() : addr.toString(); - File file=new File(dir, filename + SUFFIX); - if(log.isTraceEnabled()) - log.trace("removing " + file); - file.delete(); - } - catch(Throwable e) { - log.error("failure removing data", e); + if(log.isDebugEnabled()) { + log.debug("remove : "+clustername); } + + String filename=addr instanceof UUID? ((UUID)addr).toStringLong() : addr.toString(); + File file=new File(dir, filename + SUFFIX); + deleteFile(file); } /** * Reads all information from the given directory under clustername * @return */ - protected List readAll(String clustername) { - List retval=new ArrayList(); - File dir=new File(root_dir, clustername); - if(!dir.exists()) - dir.mkdir(); - - File[] files=dir.listFiles(filter); - if(files != null) { - for(File file: files) { - PingData data=readFile(file); - if(data == null) { - log.warn("failed reading " + file.getName() + ": removing it"); - file.delete(); - } - else - retval.add(data); - } - } - return retval; + protected synchronized List readAll(String clustername) { + List retval=new ArrayList(); + File dir=new File(root_dir, clustername); + if(!dir.exists()) + dir.mkdir(); + + log.info("reading all : "+clustername); + File[] files=dir.listFiles(filter); + if(files != null) { + for(File file: files) { + PingData data = null; + //implementing a simple spin lock doing a few attempts to read the file + //this is done since the file may be written in concurrency and may therefore not be readable + for(int i=0; i < 3; i++) { + data=null; + if(file.exists()) { + data=readFile(file); + } + if (data!=null){ + break; + } else { + try { + Thread.sleep(100); + } catch (InterruptedException ie) { + } + } + + } + + if(data == null) { + log.warn("failed parsing content in " + file.getAbsolutePath() + ": removing it from " + clustername); + deleteFile(file); + } + else { + retval.add(data); + log.trace("new data/node " + clustername); + } + } + } + return retval; } - protected static PingData readFile(File file) { - PingData retval=null; - DataInputStream in=null; - - try { - in=new DataInputStream(new FileInputStream(file)); - PingData tmp=new PingData(); - tmp.readFrom(in); - return tmp; - } - catch(Exception e) { - } - finally { - Util.close(in); - } - return retval; - } - - protected void writeToFile(PingData data, String clustername) { - DataOutputStream out=null; + private synchronized PingData readFile(File file) { + PingData retval=null; + DataInputStream in=null; + + try { + in=new DataInputStream(new FileInputStream(file)); + PingData tmp=new PingData(); + tmp.readFrom(in); + return tmp; + } + catch(Exception e) { + log.debug("failed to read file : "+file.getAbsolutePath(), e); + } + finally { + Util.close(in); + } + return retval; + } + + protected synchronized void writeToFile(PingData data, String clustername) { File dir=new File(root_dir, clustername); if(!dir.exists()) dir.mkdir(); - - String filename=addressAsString(local_addr); - File file=new File(dir, filename + SUFFIX); - file.deleteOnExit(); - - try { - out=new DataOutputStream(new FileOutputStream(file)); - data.writeTo(out); - } - catch(Exception e) { + + if (data == null) { + return; } - finally { - Util.close(out); - } - } + String filename=addressAsString(local_addr); + + //first write all data to a temporary file + //this is because the writing can be very slow under some circumstances + File tmpFile = writeToTempFile(dir, data); + if (tmpFile != null) { + File destination = new File(dir, filename + SUFFIX); + + try { + //do a file move, this is much faster and could be considered atomic on most operating systems + FileChannel src_ch = new FileInputStream(tmpFile).getChannel(); + FileChannel dest_ch = new FileOutputStream(destination).getChannel(); + src_ch.transferTo(0, src_ch.size(), dest_ch); + src_ch.close(); + dest_ch.close(); + //delete the tmp file + deleteFile(tmpFile); + if(log.isTraceEnabled()) { + log.trace("Moved: "+tmpFile.getName()+"->"+destination.getName()); + } + } catch (IOException ioe) { + log.error("attempt to move failed at " + clustername + " : "+tmpFile.getName()+"->"+destination.getName(), ioe); + } + finally { + if (destination.exists()) { + destination.deleteOnExit(); + } + if (tmpFile.exists()) { + tmpFile.deleteOnExit(); + } + } + } + } protected class WriterTask implements Runnable { public void run() { @@ -241,6 +282,61 @@ public class FILE_PING extends Discovery { } } + /** + * Attempts to delete the provided file.
+ * Logging is performed on the result + * @param file + * @return + */ + private boolean deleteFile(File file) { + boolean result = true; + if(log.isTraceEnabled()) { + log.trace("Attempting to delete file : "+file.getAbsolutePath()); + } + + if(file != null) { + try { + result = file.delete(); + } + catch(Exception e) { + log.error("Failed to delete file: "+file.getAbsolutePath(), e); + } + log.trace("Delete file result: "+file.getAbsolutePath() +" : "+result); + } + return result; + } + + + /** + * Writes the data to a temporary file.
+ * The file is stored in the same directory as the other cluster files but is given the .tmp suffix + * @param dir The cluster file dir + * @param data the data to write + * @return + */ + private File writeToTempFile(File dir, PingData data) { + DataOutputStream out=null; + + String filename=addressAsString(local_addr); + File file=new File(dir, filename + ".tmp"); + + try { + out=new DataOutputStream(new FileOutputStream(file)); + data.writeTo(out); + Util.close(out); + if(log.isTraceEnabled()) { + log.trace("Stored temporary file: "+file.getAbsolutePath()); + } + } + catch(Exception e) { + Util.close(out); + log.error("Failed to write temporary file: "+file.getAbsolutePath()); + deleteFile(file); + file = null; + } + + return file; + } } \ No newline at end of file -- 1.7.9.6