/* The contents of this file are subject to the license and copyright terms
* detailed in the license directory at the root of the source tree (also
* available online at http://fedora-commons.org/license/).
*/
package org.fcrepo.server.journal.readerwriter.multicast.rmi;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.rmi.AlreadyBoundException;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import org.fcrepo.server.journal.JournalException;
import org.fcrepo.server.journal.readerwriter.multicast.TransportOutputFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A free-standing RMI server that receives journal messages from an
* RmiTransport and writes them to files in a specified directory.
* <p>
* Note: command-line arguments are specified in
* {@link RmiJournalReceiverArguments}.
*
* @author jblake
* @version $Id: RmiJournalReceiver.java,v 1.4 2007/06/21 15:59:53 jblake Exp $
*/
@SuppressWarnings("serial")
public class RmiJournalReceiver
extends UnicastRemoteObject
implements RmiJournalReceiverInterface {
private static final Logger logger =
LoggerFactory.getLogger(RmiJournalReceiver.class);
/** The parsed arguments from the command line. */
private final RmiJournalReceiverArguments arguments;
/** The directory for journal files. */
private final File directory;
/** The journal file that is currently open, or null if no file is open. */
private TransportOutputFile journalFile;
/**
* The number of {@link #writeText(String, String) writeText} calls that
* have been made against the currently open file - used in calculating the
* itemHash.
*/
private long itemIndex;
/** A {@link FileWriter} on the current journal file. */
private Writer writer;
/** The repository hash that was supplied when the current file was opened. */
private String currentRepositoryHash;
/**
* On creation, parse the arguments.
*/
public RmiJournalReceiver(RmiJournalReceiverArguments arguments)
throws RemoteException {
super(arguments.getServerPortNumber());
this.arguments = arguments;
directory = arguments.getDirectoryPath();
}
/**
* Create an RMI registry, and bind this object to the expected name.
*/
public void exportAndBind() throws RemoteException, AlreadyBoundException,
InterruptedException {
Registry registry =
LocateRegistry
.createRegistry(arguments.getRegistryPortNumber());
registry.rebind(RMI_BINDING_NAME, this);
Thread.sleep(2000);
logger.info("RmiJournalReceiver is ready - journal directory is '"
+ arguments.getDirectoryPath().getAbsolutePath() + "'");
}
/**
* Request to open a file. Check that:
* <ul>
* <li>a file is not already open,</li>
* <li>we can create a {@link TransportOutputFile}, and open a
* {@link Writer} on it.</li>
* </ul>
*/
public void openFile(String repositoryHash, String filename)
throws JournalException {
if (journalFile != null) {
throw logAndGetException("Attempting to open file '" + filename
+ "' when file '" + journalFile.getName()
+ "' has not been closed.");
}
try {
journalFile = new TransportOutputFile(directory, filename);
writer = journalFile.open();
} catch (IOException e) {
throw logAndGetException("Problem opening" + filename + "'", e);
}
currentRepositoryHash = repositoryHash;
itemIndex = 0;
logger.debug("opened file '" + filename + "', hash is '" + repositoryHash
+ "'");
}
/**
* Request to write text to the current journal file. Check that:
* <ul>
* <li>a file is open,</li>
* <li>the supplied indexedHash matches the one we calculate,</li>
* <li>the write is successful.</li>
* </ul>
* Increment the itemIndex after a successful write.
*/
public void writeText(String indexedHash, String text)
throws JournalException {
if (journalFile == null) {
throw logAndGetException("Attempting to write when no file "
+ "is open.");
}
String calculatedHash =
RmiJournalReceiverHelper
.figureIndexedHash(currentRepositoryHash, itemIndex);
if (!calculatedHash.equals(indexedHash)) {
logger.debug("calculatedHash='" + calculatedHash + "', providedHash='"
+ indexedHash + "'");
throw logAndGetException("indexed hash is incorrect.");
}
try {
writer.append(text);
writer.flush();
} catch (IOException e) {
throw logAndGetException("Failed to write to '"
+ journalFile.getName() + "'", e);
}
logger.debug("Wrote item #" + itemIndex + " to file '"
+ journalFile.getName() + "'");
itemIndex++;
}
/**
* Request to close a file. Check that:
* <ul>
* <li>a file is open,</li>
* <li>we are able to close the file.</li>
* </ul>
*/
public void closeFile() throws JournalException {
if (journalFile == null) {
throw logAndGetException("Attempting to close a file "
+ "when no file is open.");
}
try {
writer.close();
journalFile.close();
} catch (IOException e) {
throw logAndGetException("Problem closing the file '"
+ journalFile.getName() + "'", e);
}
logger.debug("closing file: '" + journalFile.getName() + "'");
journalFile = null;
}
private JournalException logAndGetException(String message) {
logger.error(message);
return new JournalException(message);
}
private JournalException logAndGetException(String message, Throwable e) {
logger.error(message, e);
return new JournalException(message + ": " + e.toString());
}
/**
* Main routine: create the receiver from the arguments, and bind the
* receiver in the registry.
*/
public static void main(String[] args) {
try {
RmiJournalReceiverArguments arguments =
new RmiJournalReceiverArguments(args);
RmiJournalReceiver receiver = new RmiJournalReceiver(arguments);
receiver.exportAndBind();
while (true) {
Thread.sleep(60000);
}
} catch (IllegalArgumentException e) {
System.out.println("RmiJournalReciever failed: " + e.getMessage());
} catch (Exception e) {
System.out.println("RmiJournalReciever failed: ");
e.printStackTrace();
}
}
}