/* This code is part of Freenet. It is distributed under the GNU General
* Public License, version 2 (or at your option any later version). See
* http://www.gnu.org/ for further details of the GPL. */
package freenet.config;
import freenet.support.LogThresholdCallback;
import java.io.BufferedInputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import freenet.support.Logger;
import freenet.support.SimpleFieldSet;
import freenet.support.Logger.LogLevel;
import freenet.support.io.Closer;
import freenet.support.io.FileUtil;
import freenet.support.io.LineReadingInputStream;
/**
* Global Config object which persists to a file.
*
* Reads the config file into a SimpleFieldSet when created.
* During init, SubConfig's are registered, and fed the relevant parts of the SFS.
* Once initialization has finished, we check whether there are any options remaining.
* If so, we complain about them.
* And then we write the config file back out.
*/
public class FilePersistentConfig extends PersistentConfig {
final File filename;
final File tempFilename;
final protected String header;
protected final Object storeSync = new Object();
protected boolean writeOnFinished;
private static volatile boolean logMINOR;
static {
Logger.registerLogThresholdCallback(new LogThresholdCallback(){
@Override
public void shouldUpdate(){
logMINOR = Logger.shouldLog(LogLevel.MINOR, this);
}
});
}
public static FilePersistentConfig constructFilePersistentConfig(File f) throws IOException {
return constructFilePersistentConfig(f, null);
}
public static FilePersistentConfig constructFilePersistentConfig(File f, String header) throws IOException {
File filename = f;
File tempFilename = new File(f.getPath()+".tmp");
return new FilePersistentConfig(load(filename, tempFilename), filename, tempFilename, header);
}
static SimpleFieldSet load(File filename, File tempFilename) throws IOException {
boolean filenameExists = filename.exists();
boolean tempFilenameExists = tempFilename.exists();
if(filenameExists && !filename.canWrite()) {
Logger.error(FilePersistentConfig.class, "Warning: Cannot write to config file: "+filename);
System.err.println("Warning: Cannot write to config file: "+filename);
}
if(tempFilenameExists && !tempFilename.canWrite()) {
Logger.error(FilePersistentConfig.class, "Warning: Cannot write to config tempfile: "+tempFilename);
System.err.println("Warning: Cannot write to config tempfile: "+tempFilename);
}
if(filenameExists) {
if(filename.canRead() && filename.length() > 0) {
try {
return initialLoad(filename);
} catch (FileNotFoundException e) {
System.err.println("Cannot open config file "+filename+" : "+e+" - checking for temp file "+tempFilename);
} catch (EOFException e) {
System.err.println("Empty config file "+filename+" (end of file)");
}
// Other IOE's indicate a more serious problem.
} else {
// We probably won't be able to write it either.
System.err.println("Cannot read config file "+filename);
}
}
if(tempFilename.exists()) {
if(tempFilename.canRead() && tempFilename.length() > 0) {
try {
return initialLoad(tempFilename);
} catch (FileNotFoundException e) {
System.err.println("Cannot open temp config file either: "+tempFilename+" : "+e);
} // Other IOE's indicate a more serious problem.
} else {
System.err.println("Cannot read (temp) config file "+tempFilename);
throw new IOException("Cannot read (temp) config file "+tempFilename);
}
}
System.err.println("No config file found, creating new: "+filename);
return null;
}
protected FilePersistentConfig(SimpleFieldSet origFS, File fnam, File temp) throws IOException {
this(origFS, fnam, temp, null);
}
protected FilePersistentConfig(SimpleFieldSet origFS, File fnam, File temp, String header) throws IOException {
super(origFS);
this.filename = fnam;
this.tempFilename = temp;
this.header = header;
}
/** Load the config file into a SimpleFieldSet.
* @throws IOException */
private static SimpleFieldSet initialLoad(File toRead) throws IOException {
if(toRead == null) return null;
FileInputStream fis = null;
BufferedInputStream bis = null;
LineReadingInputStream lis = null;
try {
fis = new FileInputStream(toRead);
bis = new BufferedInputStream(fis);
lis = new LineReadingInputStream(bis);
// Config file is UTF-8 too!
return new SimpleFieldSet(lis, 1024*1024, 128, true, true, true); // FIXME? advanced users may edit the config file, hence true?
} finally {
Closer.close(lis);
Closer.close(bis);
Closer.close(fis);
}
}
@Override
public void register(SubConfig sc) {
super.register(sc);
}
@Override
public void store() {
if(!finishedInit) {
writeOnFinished = true;
return;
}
try {
synchronized(storeSync) {
innerStore();
}
} catch (IOException e) {
String err = "Cannot store config: "+e;
Logger.error(this, err, e);
System.err.println(err);
e.printStackTrace();
}
}
/** Don't call without taking storeSync first */
protected final void innerStore() throws IOException {
if(!finishedInit)
throw new IllegalStateException("SHOULD NOT HAPPEN!!");
SimpleFieldSet fs = exportFieldSet();
if(logMINOR)
Logger.minor(this, "fs = " + fs);
FileOutputStream fos = null;
try {
fos = new FileOutputStream(tempFilename);
synchronized(this) {
fs.setHeader(header);
fs.writeToBigBuffer(fos);
}
fos.close();
fos = null;
FileUtil.renameTo(tempFilename, filename);
}
finally {
Closer.close(fos);
}
}
public void finishedInit() {
super.finishedInit();
if(writeOnFinished) {
writeOnFinished = false;
store();
}
}
}