001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.catalina.cluster.deploy;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.io.FileInputStream;
022: import java.io.FileOutputStream;
023: import java.io.FileNotFoundException;
024:
025: /**
026: * This factory is used to read files and write files by splitting them up into smaller
027: * messages. So that entire files don't have to be read into memory.<BR>
028: * The factory can be used as a reader or writer but not both at the same time.
029: * When done reading or writing the factory will close the input or output streams
030: * and mark the factory as closed. It is not possible to use it after that.<BR>
031: * To force a cleanup, call cleanup() from the calling object.<BR>
032: * This class is not thread safe.
033: * @author Filip Hanik
034: * @version 1.0
035: */
036: public class FileMessageFactory {
037:
038: /**
039: * The number of bytes that we read from file
040: */
041: public static final int READ_SIZE = 1024 * 10; //10kb
042:
043: /**
044: * The file that we are reading/writing
045: */
046: protected File file = null;
047:
048: /**
049: * True means that we are writing with this factory.
050: * False means that we are reading with this factory
051: */
052: protected boolean openForWrite;
053:
054: /**
055: * Once the factory is used, it can not be reused.
056: */
057: protected boolean closed = false;
058:
059: /**
060: * When openForWrite=false, the input stream
061: * is held by this variable
062: */
063: protected FileInputStream in;
064:
065: /**
066: * When openForWrite=true, the output stream
067: * is held by this variable
068: */
069: protected FileOutputStream out;
070:
071: /**
072: * The number of messages we have read or written
073: */
074: protected int nrOfMessagesProcessed = 0;
075:
076: /**
077: * The total size of the file
078: */
079: protected long size = 0;
080:
081: /**
082: * The total number of packets that we split this file into
083: */
084: protected long totalNrOfMessages = 0;
085:
086: /**
087: * The bytes that we hold the data in, not thread safe.
088: */
089: protected byte[] data = new byte[READ_SIZE];
090:
091: /**
092: * Private constructor, either instantiates a factory to read or write.<BR>
093: * When openForWrite==true, then a the file, f, will be created and an output
094: * stream is opened to write to it.<BR>
095: * When openForWrite==false, an input stream is opened, the file has to exist.
096: * @param f File - the file to be read/written
097: * @param openForWrite boolean - true means we are writing to the file, false
098: * means we are reading from the file
099: * @throws FileNotFoundException - if the file to be read doesn't exist
100: * @throws IOException - if the system fails to open input/output streams to the file
101: * or if it fails to create the file to be written to.
102: */
103: private FileMessageFactory(File f, boolean openForWrite)
104: throws FileNotFoundException, IOException {
105: this .file = f;
106: this .openForWrite = openForWrite;
107: if (openForWrite) {
108: if (!file.exists())
109: file.createNewFile();
110: out = new FileOutputStream(f);
111: } else {
112: size = file.length();
113: totalNrOfMessages = (size / READ_SIZE) + 1;
114: in = new FileInputStream(f);
115: }//end if
116:
117: }
118:
119: /**
120: * Creates a factory to read or write from a file.
121: * When opening for read, the readMessage can be invoked, and when
122: * opening for write the writeMessage can be invoked.
123: * @param f File - the file to be read or written
124: * @param openForWrite boolean - true, means we are writing to the file, false means we are
125: * reading from it
126: * @throws FileNotFoundException - if the file to be read doesn't exist
127: * @throws IOException - if it fails to create the file that is to be written
128: * @return FileMessageFactory
129: */
130: public static FileMessageFactory getInstance(File f,
131: boolean openForWrite) throws FileNotFoundException,
132: IOException {
133: return new FileMessageFactory(f, openForWrite);
134: }
135:
136: /**
137: * Reads file data into the file message and sets the
138: * size, totalLength, totalNrOfMsgs and the message number<BR>
139: * If EOF is reached, the factory returns null, and closes itself,
140: * otherwise the same message is returned as was passed in.
141: * This makes sure that not more memory is ever used.
142: * To remember, neither the file message or the factory are thread safe.
143: * dont hand off the message to one thread and read the same with another.
144: * @param f FileMessage - the message to be populated with file data
145: * @throws IllegalArgumentException - if the factory is for writing or is closed
146: * @throws IOException - if a file read exception occurs
147: * @return FileMessage - returns the same message passed in as a parameter, or null if EOF
148: */
149: public FileMessage readMessage(FileMessage f)
150: throws IllegalArgumentException, IOException {
151: checkState(false);
152: int length = in.read(data);
153: if (length == -1) {
154: cleanup();
155: return null;
156: } else {
157: f.setData(data, length);
158: f.setTotalLength(size);
159: f.setTotalNrOfMsgs(totalNrOfMessages);
160: f.setMessageNumber(++nrOfMessagesProcessed);
161: return f;
162: }//end if
163: }
164:
165: /**
166: * Writes a message to file. If (msg.getMessageNumber() == msg.getTotalNrOfMsgs())
167: * the output stream will be closed after writing.
168: * @param msg FileMessage - message containing data to be written
169: * @throws IllegalArgumentException - if the factory is opened for read or closed
170: * @throws IOException - if a file write error occurs
171: * @return returns true if the file is complete and outputstream is closed, false otherwise.
172: */
173: public boolean writeMessage(FileMessage msg)
174: throws IllegalArgumentException, IOException {
175: if (!openForWrite)
176: throw new IllegalArgumentException(
177: "Can't write message, this factory is reading.");
178: out.write(msg.getData(), 0, msg.getDataLength());
179: nrOfMessagesProcessed++;
180: out.flush();
181: if (msg.getMessageNumber() == msg.getTotalNrOfMsgs()) {
182: out.close();
183: cleanup();
184: return true;
185: }//end if
186: return false;
187: }//writeMessage
188:
189: /**
190: * Closes the factory, its streams and sets all its references to null
191: */
192: public void cleanup() {
193: if (in != null)
194: try {
195: in.close();
196: } catch (Exception ignore) {
197: }
198: if (out != null)
199: try {
200: out.close();
201: } catch (Exception ignore) {
202: }
203: in = null;
204: out = null;
205: size = 0;
206: closed = true;
207: data = null;
208: nrOfMessagesProcessed = 0;
209: totalNrOfMessages = 0;
210: }
211:
212: /**
213: * Check to make sure the factory is able to perform the
214: * function it is asked to do. Invoked by readMessage/writeMessage before
215: * those methods proceed.
216: * @param openForWrite boolean
217: * @throws IllegalArgumentException
218: */
219: protected void checkState(boolean openForWrite)
220: throws IllegalArgumentException {
221: if (this .openForWrite != openForWrite) {
222: cleanup();
223: if (openForWrite)
224: throw new IllegalArgumentException(
225: "Can't write message, this factory is reading.");
226: else
227: throw new IllegalArgumentException(
228: "Can't read message, this factory is writing.");
229: }
230: if (this .closed) {
231: cleanup();
232: throw new IllegalArgumentException(
233: "Factory has been closed.");
234: }
235: }
236:
237: /**
238: * Example usage.
239: * @param args String[], args[0] - read from filename, args[1] write to filename
240: * @throws Exception
241: */
242: public static void main(String[] args) throws Exception {
243:
244: System.out
245: .println("Usage: FileMessageFactory fileToBeRead fileToBeWritten");
246: System.out
247: .println("Usage: This will make a copy of the file on the local file system");
248: FileMessageFactory read = getInstance(new File(args[0]), false);
249: FileMessageFactory write = getInstance(new File(args[1]), true);
250: FileMessage msg = new FileMessage(null, args[0], args[0]);
251: msg = read.readMessage(msg);
252: System.out.println("Expecting to write "
253: + msg.getTotalNrOfMsgs() + " messages.");
254: int cnt = 0;
255: while (msg != null) {
256: write.writeMessage(msg);
257: cnt++;
258: msg = read.readMessage(msg);
259: }//while
260: System.out.println("Actually wrote " + cnt + " messages.");
261: }///main
262:
263: public File getFile() {
264: return file;
265: }
266:
267: }
|