001: // The contents of this file are subject to the Mozilla Public License Version
002: // 1.1
003: //(the "License"); you may not use this file except in compliance with the
004: //License. You may obtain a copy of the License at http://www.mozilla.org/MPL/
005: //
006: //Software distributed under the License is distributed on an "AS IS" basis,
007: //WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
008: //for the specific language governing rights and
009: //limitations under the License.
010: //
011: //The Original Code is "The Columba Project"
012: //
013: //The Initial Developers of the Original Code are Frederik Dietz and Timo
014: // Stich.
015: //Portions created by Frederik Dietz and Timo Stich are Copyright (C) 2003.
016: //
017: //All Rights Reserved.
018:
019: package org.columba.mail.folder.mbox;
020:
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileOutputStream;
024: import java.io.IOException;
025: import java.io.InputStream;
026: import java.io.ObjectInputStream;
027: import java.io.ObjectOutputStream;
028: import java.io.RandomAccessFile;
029: import java.nio.channels.FileChannel;
030: import java.util.Enumeration;
031: import java.util.Hashtable;
032: import java.util.logging.Logger;
033:
034: import org.columba.core.io.StreamUtils;
035: import org.columba.mail.folder.AbstractMessageFolder;
036: import org.columba.mail.folder.FolderInconsistentException;
037: import org.columba.mail.folder.IDataStorage;
038: import org.columba.ristretto.io.FileSource;
039: import org.columba.ristretto.io.Source;
040:
041: public class MboxDataStorage implements IDataStorage {
042:
043: /** JDK 1.4+ logging framework logger, used for logging. */
044: private static final Logger LOG = Logger
045: .getLogger("org.columba.mail.folder.mbox");
046:
047: private static final String FROMLINE = "From \n";
048:
049: private static final byte[] TERMLINE = new byte[] { '\n' };
050:
051: protected AbstractMessageFolder folder;
052:
053: protected File mboxFile;
054:
055: protected File messageFile;
056:
057: protected Hashtable messages;
058:
059: protected Object largestUid;
060:
061: /**
062: * Constructs the MboxDataStorage.
063: *
064: * @param folder
065: */
066: public MboxDataStorage(AbstractMessageFolder folder) {
067: this .folder = folder;
068: messages = new Hashtable();
069:
070: mboxFile = new File(folder.getDirectoryFile(), folder.getId());
071:
072: messageFile = new File(folder.getDirectoryFile(), ".messages");
073:
074: if (!mboxFile.exists()) {
075: try {
076: mboxFile.createNewFile();
077: } catch (IOException e) {
078: LOG.severe(e.getLocalizedMessage());
079: }
080: } else {
081: try {
082: load();
083: } catch (IOException e) {
084: LOG.severe(e.getLocalizedMessage());
085:
086: throw new RuntimeException("Mailbox is corrupted!");
087: }
088: }
089:
090: }
091:
092: /**
093: * @see org.columba.mail.folder.IDataStorage#removeMessage(java.lang.Object)
094: */
095: public void removeMessage(Object uid) throws Exception {
096: MboxMessage message = (MboxMessage) messages.remove(uid);
097:
098: if (uid.equals(largestUid)) {
099: FileChannel channel = new RandomAccessFile(mboxFile, "rw")
100: .getChannel();
101: channel
102: .truncate(mboxFile.length()
103: - (message.getLength() + FROMLINE.length() + TERMLINE.length));
104: channel.close();
105: } else {
106:
107: int intUid = ((Integer) uid).intValue();
108:
109: deleteFilePart(mboxFile, message.getStart()
110: - FROMLINE.length(), message.getLength()
111: + FROMLINE.length() + TERMLINE.length);
112:
113: // update message starts of following messages
114: Enumeration uids = messages.keys();
115: while (uids.hasMoreElements()) {
116: Integer actUid = (Integer) uids.nextElement();
117:
118: if (actUid.intValue() > intUid) {
119: MboxMessage m = (MboxMessage) messages.get(actUid);
120: m
121: .setStart(m.getStart()
122: - (message.getLength()
123: + FROMLINE.length() + TERMLINE.length));
124: }
125: }
126: }
127: }
128:
129: /**
130: * @see org.columba.mail.folder.IDataStorage#getMessageSource(java.lang.Object)
131: */
132: public Source getMessageSource(Object uid) throws Exception {
133:
134: if (!exists(uid))
135: throw new FolderInconsistentException();
136: MboxMessage message = (MboxMessage) messages.get(uid);
137:
138: FileInputStream in = new FileInputStream(mboxFile);
139: in.skip(message.getStart());
140:
141: File tempFile = File.createTempFile("mbox_message", ".tmp");
142: tempFile.deleteOnExit();
143:
144: FileOutputStream out = new FileOutputStream(tempFile);
145:
146: StreamUtils.streamCopy(in, out, (int) message.getLength());
147:
148: in.close();
149: out.close();
150:
151: return new FileSource(tempFile);
152: }
153:
154: /**
155: * @see org.columba.mail.folder.IDataStorage#getMessageStream(java.lang.Object)
156: */
157: public InputStream getMessageStream(Object uid) throws Exception {
158: MboxMessage message = (MboxMessage) messages.get(uid);
159:
160: FileInputStream in = new FileInputStream(mboxFile);
161: in.skip(message.getStart());
162:
163: File tempFile = File.createTempFile("mbox_message", ".tmp");
164: tempFile.deleteOnExit();
165:
166: FileOutputStream out = new FileOutputStream(tempFile);
167:
168: StreamUtils.streamCopy(in, out, (int) message.getLength());
169:
170: in.close();
171: out.close();
172:
173: return new FileInputStream(tempFile);
174: }
175:
176: /**
177: * @see org.columba.mail.folder.IDataStorage#saveMessage(java.lang.Object,
178: * java.io.InputStream)
179: */
180: public void saveMessage(Object uid, InputStream source)
181: throws Exception {
182: FileOutputStream out = new FileOutputStream(mboxFile, true);
183: out.write(FROMLINE.getBytes("US-ASCII"));
184:
185: long pos = mboxFile.length();
186: long length = StreamUtils.streamCopy(source, out);
187:
188: out.write(TERMLINE);
189: out.close();
190:
191: messages.put(uid, new MboxMessage(uid, pos, length));
192:
193: largestUid = uid;
194: }
195:
196: /**
197: * @see org.columba.mail.folder.IDataStorage#getMessageCount()
198: */
199: public int getMessageCount() {
200: return messages.size();
201: }
202:
203: /**
204: * @see org.columba.mail.folder.IDataStorage#exists(java.lang.Object)
205: */
206: public boolean exists(Object uid) throws Exception {
207: return messages.containsKey(uid);
208: }
209:
210: /**
211: * @see org.columba.mail.folder.IDataStorage#getMessageUids()
212: */
213: public Object[] getMessageUids() {
214: return messages.keySet().toArray();
215: }
216:
217: /**
218: *
219: *
220: * @param file
221: * @param startpos
222: * @param length
223: * @throws IOException
224: */
225: protected void deleteFilePart(File file, long startpos, long length)
226: throws IOException {
227: RandomAccessFile ramFile = new RandomAccessFile(file, "rw");
228: long oldlength = file.length();
229:
230: FileChannel channel1 = ramFile.getChannel();
231: FileChannel channel2 = new FileInputStream(file).getChannel();
232:
233: channel2.position(startpos + length);
234: channel1.transferFrom(channel2, startpos, oldlength
235: - (length + startpos));
236: channel2.close();
237:
238: channel1.truncate(oldlength - length);
239: channel1.close();
240: }
241:
242: protected void load() throws IOException {
243: ObjectInputStream in = new ObjectInputStream(
244: new FileInputStream(messageFile));
245: MboxMessage message;
246:
247: int size = in.readInt();
248:
249: messages = new Hashtable(size);
250: for (int i = 0; i < size; i++) {
251: message = new MboxMessage(new Integer(in.readInt()), in
252: .readLong(), in.readLong());
253: messages.put(message.getUid(), message);
254: }
255:
256: in.close();
257: }
258:
259: public void save() throws IOException {
260: ObjectOutputStream out = new ObjectOutputStream(
261: new FileOutputStream(messageFile, false));
262: MboxMessage message;
263:
264: int size = messages.size();
265: out.writeInt(size);
266:
267: Enumeration message_enum = messages.elements();
268:
269: for (int i = 0; i < size; i++) {
270: message = (MboxMessage) message_enum.nextElement();
271:
272: out.writeInt(((Integer) message.getUid()).intValue());
273: out.writeLong(message.getStart());
274: out.writeLong(message.getLength());
275: }
276:
277: out.close();
278: }
279:
280: }
|