package mireka.pop.store;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* UidManager retrieves and updates the content of the file associated with each
* maildrop, which contains the last allocated UID.
*/
class UidManager {
private final Logger logger = LoggerFactory.getLogger(UidManager.class);
private long highestAllocatedUid;
private final File file;
private final File tempFile;
private boolean isInitialized;
UidManager(File dir) {
file = new File(dir, "uid.txt");
tempFile = new File(dir, "temp.uid.txt");
}
void createInitialUidFile() throws InvalidUidFileException {
if (file.exists()) {
boolean success = file.delete();
if (success) {
logger.warn("Uid file already existed, "
+ "it is successfully removed now " + file);
} else {
throw new InvalidUidFileException(
"Cannot remove already existing uid file, "
+ "maildrop is invalid " + file);
}
}
try {
writeUidFile(file);
} catch (InvalidUidFileException e) {
throw new InvalidUidFileException(
"Initial uid file cannot be created, maildrop is invalid "
+ file);
}
}
private void writeUidFile(File file) throws InvalidUidFileException {
Writer writer;
try {
writer = new OutputStreamWriter(new FileOutputStream(file));
} catch (FileNotFoundException e) {
throw new InvalidUidFileException("Cannot write uid file " + file,
e);
}
try {
writer.append(highestAllocatedUid + "+");
} catch (IOException e) {
throw new InvalidUidFileException("Error while writing uid file "
+ file, e);
} finally {
try {
writer.close();
} catch (IOException e) {
throw new InvalidUidFileException("Cannot close uid file "
+ file);
}
}
}
void init() throws InvalidUidFileException {
try {
highestAllocatedUid = readUidFile(file);
} catch (InvalidUidFileException e) {
logger.warn("Cannot find valid uid file, trying to recover", e);
if (tempFile.exists()) {
logger.warn("Temporary uid file is found");
if (file.exists()) {
boolean success = file.delete();
if (success) {
logger.warn("Invalid original uid file is "
+ "successfully deleted");
} else {
logger.error("Uid file does exist but it is "
+ "invalid and it cannot "
+ "be deleted, maildrop is invalid");
throw e;
}
}
boolean success = tempFile.renameTo(file);
if (!success) {
logger.error("Cannot move temporary uid file, maildrop is invalid");
throw e;
}
try {
highestAllocatedUid = readUidFile(file);
logger.error("Uid file is successfully recovered");
} catch (InvalidUidFileException e1) {
logger.error(
"Temporary uid file was invalid too, maildrop is invalid",
e1);
throw e;
}
} else {
logger.error("No temporary uid file exists, maildrop is invalid");
throw e;
}
}
if (tempFile.exists()) {
boolean success = tempFile.delete();
if (!success)
throw new InvalidUidFileException(
"Temporary uid file cannot be deleted, maildrop is invalid");
}
isInitialized = true;
}
private long readUidFile(File file) throws InvalidUidFileException {
InputStreamReader reader;
try {
reader =
new InputStreamReader(new FileInputStream(file), "US-ASCII");
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("Assertion failed");
} catch (FileNotFoundException e) {
throw new InvalidUidFileException(e);
}
try {
StringBuilder builder = new StringBuilder();
char[] buffer = new char[32];
int cRead;
while (-1 != (cRead = reader.read(buffer))) {
builder.append(buffer, 0, cRead);
}
String content = builder.toString();
if (content.length() < 2)
throw new InvalidUidFileException("Uid file too short: " + file);
if (content.charAt(content.length() - 1) != '+')
throw new InvalidUidFileException(
"Uid file has no ending '+': " + file);
try {
return Long
.parseLong(content.substring(0, content.length() - 1));
} catch (NumberFormatException e) {
throw new InvalidUidFileException(
"Uid file does not contain number: " + file, e);
}
} catch (IOException e) {
throw new InvalidUidFileException(e);
} finally {
try {
reader.close();
} catch (IOException e) {
logger.warn("Cannot close uid file " + file, e);
}
}
}
long allocateUid() throws InvalidUidFileException {
if (!isInitialized)
throw new IllegalStateException();
highestAllocatedUid++;
writeUidFile(tempFile);
boolean success = file.delete();
if (!success)
throw new InvalidUidFileException("Cannot delete uid file " + file);
success = tempFile.renameTo(file);
if (!success) {
highestAllocatedUid--;
throw new InvalidUidFileException(
"Cannot rename temporary file to final file " + tempFile);
}
return highestAllocatedUid;
}
}