blob: dc0fd51829192a60039594aaf0a6aced1d2ac22f [file] [log] [blame]
/*
* Copyright 1999,2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.catalina.cluster.deploy;
import java.io.File;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileNotFoundException;
/**
* This factory is used to read files and write files by splitting them up into smaller
* messages. So that entire files don't have to be read into memory.<BR>
* The factory can be used as a reader or writer but not both at the same time.
* When done reading or writing the factory will close the input or output streams
* and mark the factory as closed. It is not possible to use it after that.<BR>
* To force a cleanup, call cleanup() from the calling object.<BR>
* This class is not thread safe.
* @author Filip Hanik
* @version 1.0
*/
public class FileMessageFactory {
/**
* The number of bytes that we read from file
*/
public static final int READ_SIZE = 1024*10; //10kb
/**
* The file that we are reading/writing
*/
protected File file = null;
/**
* True means that we are writing with this factory.
* False means that we are reading with this factory
*/
protected boolean openForWrite;
/**
* Once the factory is used, it can not be reused.
*/
protected boolean closed = false;
/**
* When openForWrite=false, the input stream
* is held by this variable
*/
protected FileInputStream in;
/**
* When openForWrite=true, the output stream
* is held by this variable
*/
protected FileOutputStream out;
/**
* The number of messages we have read or written
*/
protected int nrOfMessagesProcessed = 0;
/**
* The total size of the file
*/
protected long size = 0;
/**
* The total number of packets that we split this file into
*/
protected long totalNrOfMessages = 0;
/**
* The bytes that we hold the data in, not thread safe.
*/
protected byte[] data = new byte[READ_SIZE];
/**
* Private constructor, either instantiates a factory to read or write.<BR>
* When openForWrite==true, then a the file, f, will be created and an output
* stream is opened to write to it.<BR>
* When openForWrite==false, an input stream is opened, the file has to exist.
* @param f File - the file to be read/written
* @param openForWrite boolean - true means we are writing to the file, false
* means we are reading from the file
* @throws FileNotFoundException - if the file to be read doesn't exist
* @throws IOException - if the system fails to open input/output streams to the file
* or if it fails to create the file to be written to.
*/
private FileMessageFactory(File f, boolean openForWrite)
throws FileNotFoundException, IOException{
this.file = f;
this.openForWrite = openForWrite;
if ( openForWrite ) {
if (!file.exists()) file.createNewFile();
out = new FileOutputStream(f);
} else {
size = file.length();
totalNrOfMessages = (size / READ_SIZE) + 1;
in = new FileInputStream(f);
}//end if
}
/**
* Creates a factory to read or write from a file.
* When opening for read, the readMessage can be invoked, and when
* opening for write the writeMessage can be invoked.
* @param f File - the file to be read or written
* @param openForWrite boolean - true, means we are writing to the file, false means we are
* reading from it
* @throws FileNotFoundException - if the file to be read doesn't exist
* @throws IOException - if it fails to create the file that is to be written
* @return FileMessageFactory
*/
public static FileMessageFactory getInstance(File f, boolean openForWrite)
throws FileNotFoundException, IOException {
return new FileMessageFactory(f,openForWrite);
}
/**
* Reads file data into the file message and sets the
* size, totalLength, totalNrOfMsgs and the message number<BR>
* If EOF is reached, the factory returns null, and closes itself,
* otherwise the same message is returned as was passed in.
* This makes sure that not more memory is ever used.
* To remember, neither the file message or the factory are thread safe.
* dont hand off the message to one thread and read the same with another.
* @param f FileMessage - the message to be populated with file data
* @throws IllegalArgumentException - if the factory is for writing or is closed
* @throws IOException - if a file read exception occurs
* @return FileMessage - returns the same message passed in as a parameter, or null if EOF
*/
public FileMessage readMessage(FileMessage f) throws IllegalArgumentException, IOException {
checkState(false);
int length = in.read(data);
if ( length == -1 ) {
cleanup();
return null;
} else {
f.setData(data, length);
f.setTotalLength(size);
f.setTotalNrOfMsgs(totalNrOfMessages);
f.setMessageNumber(++nrOfMessagesProcessed);
return f;
}//end if
}
/**
* Writes a message to file. If (msg.getMessageNumber() == msg.getTotalNrOfMsgs())
* the output stream will be closed after writing.
* @param msg FileMessage - message containing data to be written
* @throws IllegalArgumentException - if the factory is opened for read or closed
* @throws IOException - if a file write error occurs
* @return returns true if the file is complete and outputstream is closed, false otherwise.
*/
public boolean writeMessage(FileMessage msg) throws IllegalArgumentException, IOException {
if ( !openForWrite ) throw new IllegalArgumentException("Can't write message, this factory is reading.");
out.write(msg.getData(),0,msg.getDataLength());
nrOfMessagesProcessed++;
out.flush();
if ( msg.getMessageNumber() == msg.getTotalNrOfMsgs() ) {
out.close();
cleanup();
return true;
}//end if
return false;
}//writeMessage
/**
* Closes the factory, its streams and sets all its references to null
*/
public void cleanup() {
if ( in != null ) try { in.close(); } catch ( Exception ignore ){}
if ( out != null ) try { out.close(); } catch ( Exception ignore ){}
in = null;
out = null;
size = 0;
closed = true;
data = null;
nrOfMessagesProcessed = 0;
totalNrOfMessages = 0;
}
/**
* Check to make sure the factory is able to perform the
* function it is asked to do. Invoked by readMessage/writeMessage before
* those methods proceed.
* @param openForWrite boolean
* @throws IllegalArgumentException
*/
protected void checkState(boolean openForWrite) throws IllegalArgumentException {
if ( this.openForWrite != openForWrite ) {
cleanup();
if ( openForWrite )
throw new IllegalArgumentException("Can't write message, this factory is reading.");
else
throw new IllegalArgumentException("Can't read message, this factory is writing.");
}
if ( this.closed ) {
cleanup();
throw new IllegalArgumentException("Factory has been closed.");
}
}
/**
* Example usage.
* @param args String[], args[0] - read from filename, args[1] write to filename
* @throws Exception
*/
public static void main(String[] args) throws Exception {
System.out.println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");
System.out.println("Usage: This will make a copy of the file on the local file system");
FileMessageFactory read = getInstance(new File(args[0]),false);
FileMessageFactory write = getInstance(new File(args[1]),true);
FileMessage msg = new FileMessage(null,args[0],args[0]);
msg = read.readMessage(msg);
System.out.println("Expecting to write " + msg.getTotalNrOfMsgs()+" messages.");
int cnt = 0;
while ( msg != null ) {
write.writeMessage(msg);
cnt++;
msg = read.readMessage(msg);
}//while
System.out.println("Actually wrote " + cnt+" messages.");
}///main
public File getFile() {
return file;
}
}